1. 程式人生 > >【引用】Android.mk簡介

【引用】Android.mk簡介

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:= helloworldLOCAL_SRC_FILES := helloworld.cinclude $(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 檔案。二、自定義變數 以下是在 Android.mk中依賴或定義的變數列表, 可以定義其他變數為自己使用,但是NDK編譯系統保留下列變數名: -以 LOCAL_開頭的名字(例如 LOCAL_MODULE) -以 PRIVATE_, NDK_ 或 APP_開頭的名字(內部使用) -小寫名字(內部使用,例如‘my-dir’)  如果為了方便在 Android.mk 中定義自己的變數,建議使用 MY_字首,一個小例子MY_SOURCES := foo.cifneq ($(MY_CONFIG_BAR),) MY_SOURCES += bar.cendifLOCAL_SRC_FILES += $(MY_SOURCES)注意:‘:=’是賦值的意思;'+='是追加的意思;‘$’表示引用某變數的值。三、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'。五、模組描述變數  下面的變數用於向編譯系統描述你的模組。你應該定義在'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_PATHLOCAL_UNSTRIPPED_PATH指定最後的目標安裝路徑.不同的檔案系統路徑用以下的巨集進行選擇:  TARGET_ROOT_OUT:表示根檔案系統。   TARGET_OUT:表示 system檔案系統。   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 檔案的開頭如此定義:LOCAL_PATH := $(call my-dir)(2)all-subdir-makefiles: 返回一個位於當前'my-dir'路徑的子目錄中的所有Android.mk的列表。例如,看下面的目錄層次:sources/foo/Android.mksources/foo/lib1/Android.mksources/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庫,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 onLOCAL_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.cLOCAL_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.cLOCAL_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.cLOCAL_MODULE := libtest_sharedTARGET_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/APPSout/target/product/generic/obj/JAVA_LIBRARIESout/target/product/generic/obj/EXECUTABLEout/target/product/generic/obj/STATIC_LIBRARYout/target/product/generic/obj/SHARED_LIBRARY每個模組的目標資料夾分別為1)APK程式:XXX_intermediates2)JAVA庫程式:XXX_intermediates這裡的XXX 3)C\C++可執行程式:XXX_intermediates 4)C\C++靜態庫: XXX_static_intermediates 5)C\C++動態庫: XXX_shared_intermediates