Android.mk的用法和基礎

一個Android.mk file用來向編譯系統描述你的源代碼。具體來說:該文件是GNU Makefile的一小部分,會被編譯系統解析一次或多次。你可以在每一個Android.mk file中定義一個或多個模塊,你也可以在幾個模塊中使用同一個源代碼文件。選項參考以下文件:build/core/config.mk,默認的值在以下文件中定義:build/core/base_rules.mk。編譯系統爲你處理許多細節問題。例如,你不需要在你的Android.mk中列出頭文件和依賴文件。NDK編譯系統將會爲你自動處理這些問題。 

最後發現還是看ndk的文檔最直接,也最全面,下面的總結主要是根據ndk提供的文檔編寫的。 

1、單一的Android.mk文件: 

Java代碼

  1. LOCAL_PATH := $(call my-dir)  

  2. include $(CLEAR_VARS)  

  3.  

  4. LOCAL_MODULE    := hello-jni  

  5. LOCAL_SRC_FILES := hello-jni.c  

  6.  

  7. include $(BUILD_SHARED_LIBRARY)  

LOCAL_PATH必須位於Android.mk文件的最開始。它是用來定位源文件的位置,$(call my-dir)的作用就是返回當前目錄的路徑。 

include $(CLEAR_VARS)的作用是清除一些變量的值,但是LOCAL_PATH除外。 

LOCAL_MODULE是用來指定當前待編譯模塊的名稱,在示例中的模塊名稱爲hello-jni 

LOCAL_SRC_FILES是用來指定參與編譯的源代碼文件。這裏只編譯hell0-jin.c 

include $(BUILD_SHARED_LIBRARY)是用來指示將當前模塊編譯爲共享庫,前綴爲lib,後綴爲.so。 
還有另外一個BUILD_STATIC_LIBRARY,是用來指示將當前模塊編譯爲靜態庫的,前綴爲.a,後綴爲.a。 

這就是一個最簡單的Android.mk的結構。可能還有另外一點需要介紹: 
LOCAL_C_INCLUDES:=include 這個是用來指定在編譯時即將使用的c頭文件的位置,以當前目錄爲起點。 

例如: 

Java代碼

  1. SDL_PATH := ../SDL        #定義一個變量  

  2.  

  3. LOCAL_C_INCLUDES +=                   #加載c頭文件的目錄  

  4. $(LOCAL_PATH)/$(SDL_PATH)/include  

  5. $(LOCAL_PATH)/../ffmpeg  




2、定義多個Android.mk文件。 
有的時候,需要編譯的模塊比較多,我們可能會將對應的模塊放置在相應的目錄中,這樣,我們可以在每個目錄中定義對應的Android.mk文件(類似於上面的寫法),最後,在根目錄放置一個Android.mk文件,內容如下: 

Java代碼

  1. include $(call all-subdir-makefiles)

只需要這一行就可以了,它的作用就是包含所有子目錄中的Android.mk文件 

3、也可以在一個Android.mk文件裏包含多個模塊。 
很直觀的想法就是將第一個Android.mk文件的內容複製一份,然後修改。我最開始也是這樣做的,但是後來出現問題了,在第二個模塊中的源碼找不到,最後還是看文檔,發現裏面已經有示例解釋了: 

Java代碼

  1. LOCAL_PATH := $(call my-dir)  

基於GNU make的工作方式,$(call my-dir)會返回在解析build腳本時,遇到的最後一個 include中涉及的目錄。 

所以,很多時候,在這個Android.mk裏面只需要調用一次$(call my-dir)就夠了,如果所有的源文件都在一個目錄中。 

如果需要的話,可以在第一次調用call my-dir的時候,將值保存下來,比如: 

Java代碼

  1. MY_LOCAL_PATH := $(call my-dir)  

  2. LOCAL_PATH := $(MY_LOCAL_PATH)  

  3.    

  4. 然後,在另外一個模塊中,繼續如下定義:  

  5. LOCAL_PATH := $(MY_LOCAL_PATH)  

在編譯一般的c源代碼時,上面的基本可以滿足了 
關於LOCAL_CFLAGS 
在某些時候,編譯源碼需要定義宏變量,這個時候,我們可以直接在對應的源碼裏面去修改,但也有一些情況,我們是沒法在別人的源碼裏定義宏變量的,這個時候,就需要使用到LOCAL_CFLAGS 了 ,舉例如下: 

Java代碼

  1. LOCAL_CFLAGS  += -D__FAVOR_BSD  

  2. 這行代碼的作用就是在原有的cflags基礎上,再定義一個宏變量__FAVOR_BSD  

類似於#define __FAVOR_BSD 

二、自定義變量 
以下是在 Android.mk中依賴或定義的變量列表, 可以定義其他變量爲自己使用,但是NDK編譯系統保留下列變量名: 

Java代碼

  1. -以 LOCAL_開頭的名字(例如 LOCAL_MODULE)  

  2. -以 PRIVATE_, NDK_ 或 APP_開頭的名字(內部使用)  

  3. -小寫名字(內部使用,例如‘my-dir’)  

 如果爲了方便在 Android.mk 中定義自己的變量,建議使用 MY_前綴,一個小例子: 

Java代碼

  1. MY_SOURCES := foo.c  

  2. ifneq ($(MY_CONFIG_BAR),)  

  3. MY_SOURCES += bar.c  

  4. endif  

  5. LOCAL_SRC_FILES += $(MY_SOURCES)  

注意:‘:=’是賦值的意思;'+='是追加的意思;‘$’表示引用某變量的值。

三、GNU Make系統變量 
 這些 GNU Make變量在你的 Android.mk 文件解析之前,就由編譯系統定義好了。注意在 
某些情況下,NDK可能分析 Android.mk 幾次,每一次某些變量的定義會有不同。 
 (1)CLEAR_VARS: 指向一個編譯腳本,幾乎所有未定義的 LOCAL_XXX 變量都在"Module-des cription"節中列出。必須在開始一個新模塊之前包含這個腳本: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. 

Java代碼

  1. android-3 -> Official Android 1.5 system images  

  2. android-4 -> Official Android 1.6 system images  

  3. 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'。 

五、模塊描述變量 
 下面的變量用於向編譯系統描述你的模塊。你應該定義在'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的,你可以使用路徑部分,例如: 

Java代碼

  1. LOCAL_SRC_FILES := foo.c toto/bar.c  

  2. 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目錄。 
 示例:

Java代碼

  1. 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指定最後的目標安裝路徑. 
不同的文件系統路徑用以下的宏進行選擇: 

Java代碼

  1. TARGET_ROOT_OUT:表示根文件系統。  

  2. TARGET_OUT:表示 system文件系統。  

  3. TARGET_OUT_DATA:表示 data文件系統。  

用法如:LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT) 
至於LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的區別,暫時還不清楚。 

七、GNU Make‘功能’宏 
GNU Make‘功能’宏,必須通過使用'$(call  )'來調用,調用他們將返回文本化的信息。 
(1)my-dir:返回當前 Android.mk 所在的目錄的路徑,相對於 NDK 編譯系統的頂層。這是有用的,在 Android.mk 文件的開頭如此定義: 

Java代碼

  1. LOCAL_PATH := $(call my-dir)  

(2)all-subdir-makefiles: 返回一個位於當前'my-dir'路徑的子目錄中的所有Android.mk的列表。 
例如,看下面的目錄層次: 

Java代碼

  1. sources/foo/Android.mk  

  2. sources/foo/lib1/Android.mk  

  3. 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的路徑 
八、 Android.mk 使用模板 
 在一個 Android.mk 中可以生成多個APK應用程序,JAVA庫,CC++可執行程序,CC++動態庫和CC++靜態庫。 
(1)編譯APK應用程序模板。 
關於編譯APK應用程序的模板請參照《Android.mk編譯APK範例》 
(2)編譯JAVA庫模板 

Java代碼

  1. LOCAL_PATH := $(call my-dir)  

  2. include $(CLEAR_VARS)  

  3. # Build all java files in the java subdirectory  

  4. LOCAL_SRC_FILES := $(call all-subdir-java-files)  

  5. # Any libraries that this library depends on  

  6. LOCAL_JAVA_LIBRARIES := android.test.runner  

  7. # The name of the jar file to create  

  8. LOCAL_MODULE := sample  

  9. # Build a static jar file.  

  10. include $(BUILD_STATIC_JAVA_LIBRARY)  

 注:LOCAL_JAVA_LIBRARIES := android.test.runner表示生成的JAVA庫的jar文件名
(3)編譯C/C++應用程序模板如下: 

Java代碼

  1. LOCAL_PATH := $(call my-dir)  

  2. #include $(CLEAR_VARS)  

  3. LOCAL_SRC_FILES := main.c  

  4. LOCAL_MODULE := test_exe  

  5. #LOCAL_C_INCLUDES :=  

  6. #LOCAL_STATIC_LIBRARIES :=  

  7. #LOCAL_SHARED_LIBRARIES :=  

  8. include $(BUILD_EXECUTABLE)  


注:‘:=’是賦值的意思,'+='是追加的意思,‘$’表示引用某變量的值
LOCAL_SRC_FILES中加入源文件路徑,LOCAL_C_INCLUDES中加入需要的頭文件搜索路徑 
LOCAL_STATIC_LIBRARIES 加入所需要鏈接的靜態庫(*.a)的名稱, 
LOCAL_SHARED_LIBRARIES 中加入所需要鏈接的動態庫(*.so)的名稱, 
LOCAL_MODULE表示模塊最終的名稱,BUILD_EXECUTABLE 表示以一個可執行程序的方式進行編譯。 
(4)編譯CC++靜態庫 

Java代碼

  1. LOCAL_PATH := $(call my-dir)  

  2. include $(CLEAR_VARS)  

  3. LOCAL_SRC_FILES :=  

  4. helloworld.c  

  5. LOCAL_MODULE:= libtest_static  

  6. #LOCAL_C_INCLUDES :=  

  7. #LOCAL_STATIC_LIBRARIES :=  

  8. #LOCAL_SHARED_LIBRARIES :=  

  9. include $(BUILD_STATIC_LIBRARY)  


和上面相似,BUILD_STATIC_LIBRARY 表示編譯一個靜態庫。 
(5)編譯CC++動態庫的模板 

Java代碼

  1. LOCAL_PATH := $(call my-dir)  

  2. include $(CLEAR_VARS)  

  3. LOCAL_SRC_FILES := helloworld.c  

  4. LOCAL_MODULE := libtest_shared  

  5. TARGET_PRELINK_MODULES := false  

  6. #LOCAL_C_INCLUDES :=  

  7. #LOCAL_STATIC_LIBRARIES :=  

  8. #LOCAL_SHARED_LIBRARIES :=  

  9. include $(BUILD_SHARED_LIBRARY)  


和上面相似,BUILD_SHARED_LIBRARY 表示編譯一個共享庫。 
以上三者的生成結果分別在如下目錄中,generic 依具體 target 會變: 

Java代碼

  1. out/target/product/generic/obj/APPS  

  2. out/target/product/generic/obj/JAVA_LIBRARIES  

  3. out/target/product/generic/obj/EXECUTABLE  

  4. out/target/product/generic/obj/STATIC_LIBRARY  

  5. out/target/product/generic/obj/SHARED_LIBRARY  

每個模塊的目標文件夾分別爲: 
1)APK程序:XXX_intermediates 
2)JAVA庫程序:XXX_intermediates 
這裏的XXX 
3)CC++可執行程序:XXX_intermediates 
4)CC++靜態庫: XXX_static_intermediates 
5)CC++動態庫: XXX_shared_intermediates


轉載  http://www.bdqn.cn/news/201307/10450.shtml

發佈了30 篇原創文章 · 獲贊 4 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章