原文網址:http://blog.csdn.net/smfwuxiao/article/details/8530742java
Android.mk文件用來告訴NDK編譯系統,應該如何編譯這些源碼。更確切地說,該文件其實就是一個小型的Makefile。該文件會被NDK的編譯工具解析屢次,因此要注意不要過多使用環境變量,以避免第一次解析時產生的變量影響後面的解析。Android.mk把源碼組織成不一樣的模塊,每一個模塊能夠是一個靜態庫也能夠是一個動態庫。動態庫纔會被拷貝到安裝包中,靜態庫只能用於編譯生成動態庫。android
同一個Android.mk文件能夠定義多個模塊,不一樣的模塊能夠共用同一個源文件。shell
注意,NDK所使用的Android.mk文件的語法與Android操做系統的開放源碼中的Android.mk的語法很是接近,可是兩個編譯系統對Android.mk的使用方法不一樣,這是爲了方便應用程序開發人員複用之前的代碼。安全
在詳細討論Android.mk文件的語法以前,先看一個簡單的 「hello JNI「 的例子,文件位於 apps/hello-jni/project。其中的 src 子目錄存放Android工程的java源碼,jni子目錄存放C/C++源碼文件,即 jni/hello-jni.c (實現了一個返回字符串給虛擬機的函數)。jni/Android.mk 文件是這個模塊的編譯腳本,內容以下:架構
以上內容解釋以下:app
LOCAL_PATH := $(call my-dir)函數
每一個Android.mk文件都必須在開頭定義 LOCAL_PATH 變量。這個變量被用來尋找C/C++源文件。在該例中,my-dir 是一個由編譯系統提供的宏函數,用於返回Android.mk所在目錄的路徑。工具
include $(CLEAR_VARS)測試
CLEAR_VARS是編譯系統預約義的一個變量,它指向一個特殊的Makefile,這個Makefile負責清除 LOCAL_xxx 的變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES 等)但不會清除 LOCAL_PATH。之因此須要清理這些變量是由於全部的編譯控制文件是在一趟make執行過程當中完成的,而全部的變量都是全局的,會對其餘Android.mk文件產生影響。優化
LOCAL_MODULE := hello-jni
LOCAL_MODULE 用來給每一個模塊定義一個名字,不一樣模塊的名字不能相同,不能有空格。這裏的名字會傳給NDK編譯系統,而後加上lib前綴和.so後綴 (例如,變成libhello-jni.so)。注意,若是你在LOCAL_MODULE定義中本身加上了lib前綴,則ndk在處理的時候就不會再加上lib前綴了(爲了兼容Android系統的一些源碼)。
LOCAL_SRC_FILES := hello-jni.c
在LOCAL_SRC_FILES 變量裏面列舉出對應於同一個模塊的、要編譯的那些文件,這裏不要把頭文件加進來,編譯系統能夠自動檢測頭文件依賴關係。默認狀況下,C++源碼文件的擴展名應該是cpp,若是想修改的話,將變量LOCAL_CPP_EXTENSION修改成你想要的擴展名,注意句點。例如:LOCAL_CPP_EXTENSION := .cxx
include $(BUILD_SHARED_LIBRARY)
這個 BUILD_SHARED_LIBRARY也是預約義的變量,也是指向一個Makefile,負責將你在 LOCAL_XXX 等變量中定義信息收集起來,肯定要編譯的文件,如何編譯。若是要編譯的是靜態庫而不是動態庫,則能夠用 BUILD_STATIC_LIBRARY。
在NDK安裝目錄的samples目錄下有更加豐富的例子,裏面都有詳細的註釋。
下面這些變量是你能夠直接使用或者應該由你來定義的。你也能夠定義本身的變量,可是不能用如下NDK所保留的變量名:
以 LOCAL_ 開頭的名字(例如,LOCAL_MODULE)
以 PRIVATE_, NDK_,APP_ 開頭的名字(供NDK內部使用)
小寫字母的變量名也不能使用(供NDK內部使用,例如 my-dir)
例如,你能夠隨便用 MY_ 開頭的變量名:
下面這些變量是ndk提早定義好的變量。有時ndk會解析同一個Android.mk文件屢次,每次解析時,這些變量的值可能不相同。
指向一個特殊的Makefile,負責清理 LOCAL_XXX 變量(LOCAL_PATH除外)。通常在定義新模塊以前使用這個變量,用法:
include $(CLEAR_VARS)
該變量實際指向了一個Makefile,用來把全部名爲 LOCAL_XXX的變量中的信息收集起來,而後肯定如何把你提供的源碼編譯成目標模塊。用法:include $(BUILD_SHARED_LIBRARY) 默認文件名:lib<LOCAL_MODULE>.so
相似於BUILD_SHARED_LIBRARY,不過它用來編譯靜態庫。靜態庫不會被拷貝到你的安裝包中去,它每每用來編譯其餘動態庫。用法:include $(BUILD_STATIC_LIBRARY) 默認文件名:lib<LOCAL_MODULE>.a
該變量指向一個已編譯好的共享庫。與BUILD_SHARED_LIBRARY和BUILD_STATIC_LIBRARY不一樣,此時相應的LOCAL_SRC_FILES再也不指定源文件,而是指向這個預編譯共享庫文件(例如 foo/libfoo.so)。能夠在其餘模塊中,經過使用LOCAL_PREBUILTS變量來引用這個預編譯模塊。參考Prebuilt。
與PREBUILT_SHARED_LIBRARY相同,只不過這裏是靜態庫。 參考 Prebuilt。
目標CPU架構的名字,與Android操做系統的CPU架構名一致。若是想兼容全部ARM的CPU,能夠用 「arm」 這個名字。
目標Android平臺的名字。例如 android-3 對應的是 Android 1.5 系統鏡像(Cupcake)。全部系統鏡像的名字和相應的系統鏡像可參考Stable APIs。
目標CPU和ABI組合的名字,目前只有2個值能夠用:
armeabi 對於ARMv5TE
armeabi-v7a
注意,一直到Android NDK 1.6_r1,這裏的值都是用「arm」。然而,該值已被從新定義以更好地匹配Android平臺內部所使用的。
關於架構和ABI及兼容性問題,參考文檔 Cpu Arch ABIs。
其餘的目標ABI會在未來的NDK版本中增長,而且是不一樣的名字。注意,全部兼容ARM的ABI的TARGET_ARCH都是arm,可是TARGET_ARCH_ABI不一樣。
目標平臺和ABI的組合,定義爲 $(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)。當你想在真機上測試一種系統鏡像時有用。該變量的默認值是android-3-armeabi。
直到Android NDK 1.6_r1,這個值一直是 android-3-arm。
下面是NDK預約義的「函數」宏,用法是 $(call <function>) ,返回的是文本信息
返回上一個被包含的Makefile的路徑,典型狀況是Android.mk文件所在的路徑。這個函數對於定義LOCAL_PATH特別有用,例如:
LOCAL_PATH := $(call my-dir)
注意:因爲make的工做原理,該函數返回的確實是上一個被包含的Makefile的路徑(也就是說返回的結果可能不是Android.mk所在目錄)。因此,包含了另一個文件以後,就不要再使用 my-dir 了。
例如,下面的例子:
上面的問題就是第二次調用my-dir的時候,獲得的是 $PATH/foo 而不是 $PATH,由於它前面有一個include語句。
所以,最好在全部include語句以前,把LOCAL_PATH定義好:
若是以爲這樣作不方便,能夠把第一次調用的結果保存到變量中,例如:
返回當前的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。
返回當前的Makefile的路徑(即該函數調用時的位置)
若是當前這個Makefile被另外一個Makefile包含,則返回那個包含了本身的Makefile的路徑(即parent)
(你猜猜......)
該函數用於按模塊名查找另外一個模塊的Android.mk文件,幷包含進來。用法以下:
$(call import-module,<name>)
上面將在 NDK_MODULE_PATH變量所指定的目錄列表中尋找名爲<name>的模塊,找到以後將包含進來。
能夠參考 Import Module。
下面這些變量用於對模塊進行描述,這些變量應該在 include $(CLEAR_VARS) 和 include $(BUILD_XXXX) 之間定義好。
這個變量表示當前文件(通常是Android.mk)所在的路徑,該變量很重要,必須定義(在Android.mk文件的開頭處定義)。常見寫法以下:
LOCAL_PATH := $(call my-dir)
該變量不會被 include $(CLEAR_VARS) 清空,因此不論Android.mk定義了幾個模塊,一個Android.mk只須要在開頭定義一次便可。
該變量定義當前模塊的名字,名字必須惟一,不能有空格。這個變量必須在 include $(BUILD_XXX) 以前定義好。默認狀況下,這裏的名字會用來獲得輸出文件的名字。例如模塊名爲foo,則獲得的輸出文件爲libfoo.so。可是,若是你要在其餘模塊的Android.mk文件或Application.mk中引用這個模塊,應該用foo這個模塊名,而不要用libfoo.so這個文件名。
該變量能夠用來重定義輸出文件的名字。默認狀況下,foo模塊獲得的靜態庫的名字爲 libfoo.a,動態庫的名字爲libfoo.so(UNIX規範)。當定義了LOCAL_MODULE_FILENAME以後,輸出文件名就是這個變量指定的名字,例如:
注意: LOCAL_MODULE_FILENAME不支持文件路徑(因此不能有斜槓),不要寫擴展名(文件路徑和文件擴展名是由編譯工具自動加上的)
該變量用來指定該模塊對應的源文件,只把須要傳給編譯器的源文件名加進LOCAL_SRC_FILES,編譯系統會自動處理頭文件依賴。這裏的文件名都是以 LOCAL_PATH 做爲當前目錄的(即相對於LOCAL_PATH目錄),例如:
LOCAL_SRC_FILES := foo.c toto/bar.c
注意:必須使用Unix風格的斜槓,Windows風格的斜槓不能正確處理。
用來定義C++代碼文件的擴展名。必須以句點開頭(即 「.」),默認值是「.cpp」,能夠修改,例如:
LOCAL_CPP_EXTENSION := .cxx
從 NDK r7 這個版本開始,該變量能夠支持多個擴展名了,例如:
LOCAL_CPP_EXTENSION := .cxx .cpp .cc
該變量用來指定C++代碼所依賴的特殊C++特性。例如,若是要告訴編譯器你的C++代碼使用了RTTI(RunTime Type Information):
LOCAL_CPP_FEATURES := rtti
若是要指定你的C++代碼使用了C++異常,則:
LOCAL_CPP_FEATURES := exceptions
該變量能夠同時指定多個特性。例如: LOCAL_CPP_FEATURES := rtti features
這個變量的做用就是在編譯模塊的時候,開啓相應的編譯器/連接器標誌。對於預編譯的文件,該變量代表該預編譯的庫依賴了這些特性,從而確保最後的連接工做正確進行。與該變量等價的作法是在LOCAL_CPPFLAGS中寫上 -frtti -fexceptions 等標誌選項。可是,推薦用這裏的方法。
一個路徑的列表,是NDK根目錄的相對路徑(LOCAL_SRC_FILES中的文件相對於LOCAL_PATH)。當編譯C/C++、彙編文件時,這些路徑將被追加到頭文件搜索路徑列表中。例如:
LOCAL_C_INCLUDES := sources/foo
或者, LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
這裏的搜索路徑會放在LOCAL_CFLAGS/LOCAL_CPPFALGS等標誌的前面。 當使用ndk-gdb的時候,LOCAL_C_INCLUDES中的路徑也會被用到。
指定當編譯C/C++源碼的時候,傳給編譯器的標誌。它通常用來指定其餘的編譯選項和宏定義。
注意:儘可能不要在Android.mk中修改優化/調試等級,由於在Application.mk中定義了相關信息以後編譯系統會自動處理這些問題。
編譯C++代碼的時候傳遞給編譯器的選項(編譯C代碼不會用這裏的選項)。最後獲得的命令行選項中,這裏指定的選項在 LOCAL_CFLAGS 指定的選項的後面。
指定應該連接到當前模塊的靜態庫(可指定多個)。當前模塊是動態庫時,該選項纔有意義。
指定的是運行時該模塊所依賴共享庫(可指定多個)。這些信息是連接階段必須的。
它是LOCAL_STATIC_LIBRARIES的變體,用來表示它對應的模塊對於linker來講應該是一個「whole archive」(見GNU linker 文檔,關於 --whole-archive的資料)。當靜態庫之間有循環依賴時,會用到這個選項。注意,當編譯動態庫時,這個選項會強行把全部的對象文件組裝到一塊兒;不過,在編譯可執行文件的時候狀況不是這樣的。
用來指定模塊編譯時的其他鏈接器標誌。例如:
LOCAL_LDLIBS := -lz
告訴連接器在加載該共享庫的時候必須連接 /system/lib/libz.so 這個共享庫。
若是想知道Android系統中有哪些共享庫能夠連接,參考 Stable APIs。
默認狀況下,當編譯一個共享庫的時候,遇到未定義符號引用就會報告一個「undefined symbol」錯誤。這有助於修復你的代碼中存在的bug。
若是由於某種緣由,必須禁止該檢測,能夠把這個變量設置爲true。注意,編譯出的共享庫有可能在加載的時候就報錯致使程序退出。
LOCAL_ARM_MODE
LOCAL_ARM_NEON
Android NDK r4增長了對「NX bit「安全特性的支持。它是默認開啓的,若是你肯定本身不須要該特性,你能夠將它關閉,即:
LOCAL_DISABLE_NO_EXECUTE := true
該變量不會修改ABI,只會在 ARMv6以上的CPU的內核上啓用。開啓該特性編譯出的代碼無需修改可運行在老的CPU上(也就是說全部ARM的CPU都能運行)。
參考信息:
http://en.wikipedia.org/wiki/NX_bit
http://www.gentoo.org/proj/en/hardened/gnu-stack.xml
這個變量定義一些C/C++編譯器flags。這些flags(標誌)會被追加到使用了這個模塊(利用LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES)的模塊的LOCAL_CFLAGS 定義中去。
假如foo模塊的聲明以下:
bar模塊依賴foo模塊,聲明以下:
所以在編譯bar模塊的時候,它的編譯器標誌就是 「-DFOO=1 -DBAR=2」。 導出的flags 加上本模塊的 LOCAL_CFLAGS,成爲最後傳給編譯器的flags。這樣修改起來就很容易。這種依賴關係是可傳遞的,例如,若是zoo依賴bar,bar依賴foo,那麼zoo就會同時有bar和foo的導出flags。
注意,編譯模塊自身時,不會使用它所導出的flags。例如在編譯上面的foo模塊時, -DFOO=1 不會傳遞給編譯器。
與 LOCAL_EXPORT_CFLAGS 相同,是跟C++相關的標誌。
與 LOCAL_EXPORT_CFLAGS 相同,可是隻用於頭文件搜索路徑。當你的共享庫有多個模塊,並且互相之間有頭文件依賴時有用。用法詳見 Import Module。
與 LOCAL_EXPORT_CFLAGS 相同,可是隻用於鏈接器的flag。注意這裏被導人的連接器標誌將追加到模塊的 LOCAL_LDLIBS。
例如當foo模塊是一個靜態庫而且代碼依賴於系統庫時,該變量很是有用。 LOCAL_EXPORT_LDLIBS 能夠用於導出該依賴:
此處在編譯bar模塊的時候,它的連接器標誌將加上一個 -llog,表示它依賴於系統提供的 liblog.so,由於它依賴 foo 模塊。
這個變量指定一個shell命令,用於過濾LOCAL_SRC_FILES 中列出的彙編文件或者LOCAL_SRC_FILES列出的文件所編譯出的彙編文件。定義該變量後,將致使如下行爲:
1)全部的C/C++源碼首先翻譯爲臨時彙編文件(若是不定義LOCAL_FILTER_ASM,則C/C++源碼直接編譯爲 obj 文件)
2)這些彙編文件被傳給 LOCAL_FILTER_ASM 所指定的shell命令處理,獲得一批新的彙編文件。
3)這些新的彙編文件再被編譯成obj文件。
換句話說,若是你定義:
則foo.c首先傳給編譯器(gcc),獲得 foo.S.orignal,而後這個 foo.S.original 被傳給你指定的過濾器(LOCAL_ASM_FILTER),獲得 foo.S,而後再傳給彙編器(例如as),獲得 foo.o。 bar.S 直接傳給過濾器獲得 bar.S.new,而後再傳給彙編器,獲得 bar.o。即在從*.S到*.o的編譯流程中增長了一個過濾的環節。
過濾器必須是獨立的shell命令,輸入文件做爲它的第一個命令行參數,輸出文件做爲第二個命令行參數,例如: