android.mk 詳解

Android.mk詳解

  想請教下Android.mk中一些屬性、方法、定義,總之就想詳細瞭解下Android.mk文件,

我先拋磚引玉。

  LOCAL_PATH

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

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:= 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 文件。

二、自定義變量

 以下是在 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)

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

三、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_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的區別,暫時還不清楚。

七、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的路徑

八、 Android.mk 使用模板

  在一個 Android.mk 中可以生成多個APK應用程序,JAVA庫,C\C++可執行程序,C\C++動態庫和C\C++靜態庫。

(1)編譯APK應用程序模板。

關於編譯APK應用程序的模板請參照《Android.mk編譯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

發佈留言