Android.mk文件語法規範及使用

from:http://blog.chinaunix.net/uid-29494667-id-4116547.htmlhtml

1.概述
Android.mk編譯文件是用來向Android NDK描述你的C,C++源代碼文件的。具體來講:
該文件是GNU Makefile的一小部分,會被編譯系統解析一次或更屢次的build系統。所以,您應儘可能減小您聲明的變量,不要認爲某些變量在解析過程當中不會被定義。
這個文件的語法容許把你的源代碼組織成模塊,一個模塊屬下列類型之一:java

靜態庫linux

共享庫android

只有共享庫將被安裝/複製到您的應用軟件包。雖然靜態庫能被用於生成共享庫。你能夠在每個Android.mk file中定義一個或多個模塊,你也能夠在幾個模塊中使用同一個源代碼文件。

2.樣例簡析架構

在描述語法細節以前,我們來看一個簡單的"hello world"的例子,好比,下面的文件:函數

sources/helloworld/helloworld.cui

sources/helloworld/Android.mkthis

'helloworld.c'是一個JNI共享庫,實現返回"hello world"字符串的原生方法。spa

相應的Android.mk文件會象下面這樣:.net

---------- cut here ------------------

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE:= helloworld

LOCAL_SRC_FILES := helloworld.c

include $(BUILD_SHARED_LIBRARY)

---------- cut here ------------------

好,咱們來解釋一下這幾行代碼:

LOCAL_PATH:= $(call my-dir)

一個Android.mk file首先必須定義好LOCAL_PATH變量。它用於在開發樹中查找源文件。在這個例子中,宏函數’my-dir’, 由編譯系統提供,用於返回當前路徑(即包含Android.mk file文件的目錄)。

include $( CLEAR_VARS)

CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE爲你清除許多LOCAL_XXX變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),

除LOCAL_PATH 。這是必要的,由於全部的編譯控制文件都在同一個GNU MAKE執行環境中,全部的變量都是全局的。

LOCAL_MODULE := helloworld

LOCAL_MODULE變量必須定義,以標識你在Android.mk文件中描述的每一個模塊。名稱必須是惟一的,並且不包含任何空格。注意編譯系統會自動產生合適的前綴和後綴,換句話說,一個被命名爲'foo'的共享庫模塊,將會生成'libfoo.so'文件。

重要注意事項

若是你把庫命名爲‘libhelloworld’,編譯系統將不會添加任何的lib前綴,也會生成libhelloworld.so,這是爲了支持來源於Android平臺的源代碼的Android.mk文件,若是你確實須要這麼作的話。

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),負責收集自從上次調用'include $(CLEAR_VARS)'以來,定義在LOCAL_XXX變量中的全部信息,而且決定編譯什麼,如何正確地去作。並根據其規則生成靜態庫。同理對於靜態庫。

3.深刻分析

下面咱們嘗試從一個小模塊逐步對android build system作一個深刻剖析。選擇的這個模塊名字叫作acp ,源碼位於build\tools\acp目錄。

後續不少模塊的編譯都須要使用到acp,根據編譯依賴通常會先編譯本模塊。固然它也須要依賴到其餘文件,須要的時候咱們再進行闡述。
acp的Android.mk比較簡單,去掉的無用代碼後,以下面所示:

Makefile代碼   收藏代碼
  1. LOCAL_PATH:= $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. LOCAL_SRC_FILES := acp.c  
  5.   
  6. LOCAL_STATIC_LIBRARIES := libhost  
  7. LOCAL_C_INCLUDES := build/libs/host/include  
  8. LOCAL_MODULE := acp  
  9. LOCAL_ACP_UNAVAILABLE := true  
  10.   
  11. include $(BUILD_HOST_EXECUTABLE)   

上面的語句大體的意思就是,使用當前路徑下的acp.c源碼,引用的include和連接的library都是host模塊,最終編譯生成一個可在當前主機運行的可執行文件,名字爲acp(linux環境)。。
這裏咱們先不談每個變量的具體含義和使用,咱們先大概看一下一個Android.mk的基本組成。

  1. LOCAL_PATH 定義了當前模塊的相對路徑,必須出如今全部的編譯模塊以前
  2. 每一個編譯模塊由include $(CLEAR_VARS) 開始,由include $(BUILD_XXX) 結束
  3. include $(CLEAR_VARS) 是一個編譯模塊的開始,它會清空除LOCAL_PATH以外的全部LOCA_XXX變量
  4. include $(BUILD_XXX) 描述了編譯目標
  5. LOCAL_SRC_FILES 定義了本模塊編譯使用的源文件,採用的是基於LOCAL_PATH的相對路徑
  6. LOCAL_MODULE 定義了本模塊的模塊名

編譯acp還須要了幾個可選的變量:

  • LOCAL_STATIC_LIBRARIES 表示編譯本模塊時須要連接的靜態庫
  • LOCAL_C_INCLUDES 表示了本模塊須要引用的include文件
  • LOCAL_ACP_UNAVAILABLE 表示是否支持acp,若是支持acp,則使用acp進行拷貝,不然使用linux cp拷貝,本模塊編譯acp,固然是不支持acp了

編譯目標

上面咱們用到include $(CLEAR_VARS)和include $(BUILD_HOST_EXECUTABLE),那麼他們是在哪裏定義的呢?除了BUILD_HOST_EXECUTABLE還有哪些BUILD_XXX目標呢?
它們的定義位於build/core/config.mk文件,固然config.mk文件定義的編譯目標也不少,下面列舉幾個經常使用的目標:
編譯目標 說明
BUILD_HOST_STATIC_LIBRARY 主機上的靜態庫
BUILD_HOST_SHARED_LIBRARY 主機上的動態庫
BUILD_HOST_EXECUTABLE 主機上的可執行文件
BUILD_STATIC_LIBRARY 目標設備上的靜態庫
BUILD_SHARED_LIBRARY 目標設備上的動態庫
BUILD_EXECUTABLE 目標設備上的可執行文件
BUILD_JAVA_LIBRARY JAVA庫
BUILD_STATIC_JAVA_LIBRARY 靜態JAVA庫
BUILD_HOST_JAVA_LIBRARY 主機上的JAVA庫
BUILD_PACKAGE APK程序
具體的每個目標,等咱們遇到的時候咱們再詳細進行講解,先重點分析 LOCAL_PATH

有人就問了,在本Android.mk中又沒有使用到LOCAL_PATH,爲何先 要定義這麼一個變量呢?爲何規定必須放在全部的include $(CLEAR_VARS)以前呢?
 
在Android.mk中咱們發現有LOCAL_SRC_FILES := acp.的定義,NDK文件中對LOCAL_SRC_FILES 的說明以下:
This is a list of source files that will be built for your module. Only list the files that will be passed to a compiler, since the build system automatically computes dependencies for you.
Note that  source files names are all relative to LOCAL_PATH and you can use path components .
所以在定義LOCAL_SRC_FILES 時已經間接的使用到了LOCAL_PATH變量,即定義LOCAL_SRC_FILES是用的基於當前路徑的相對路徑。
 
咱們接着看看爲何LOCAL_PATH的定義必需要放到全部的include $(CLEAR_VARS)以前。
LOCAL_PATH經過調用my-dir函數來獲取當前的路徑,my-dir函數的定義位於core/definitions.mk文件:
Makefile代碼   收藏代碼
  1. <span style="font-size: small;"># Figure out where we are.  
  2. define my-dir  
  3. $(strip \  
  4.   $(eval md_file_ := $$(lastword $$(MAKEFILE_LIST))) \  
  5.   $(if $(filter $(CLEAR_VARS),$(md_file_)), \  
  6.     $(error LOCAL_PATH must be set before including $$(CLEAR_VARS)) \  
  7.    , \  
  8.     $(patsubst %/,%,$(dir $(md_file_))) \  
  9.    ) \  
  10. )  
  11. endef  
請注意,這裏明確的說明了LOCAL_PATH的定義必需要放在任何include $(CLEAR_VARS)語句以前,若是不這麼作的話,編譯就直接報錯,中止不幹了。
 
但是它是怎麼判斷LOCAL_PATH的定義是在任何include $(CLEAR_VARS)語句以前呢,咱們看到有這麼一句話:
Makefile代碼   收藏代碼
  1. $(if $(filter $(CLEAR_VARS),$(md_file_))  
 這個判斷語句是個關鍵,咱們先看看CLEAR_VARS變量的定義
 
CLEAR_VARS
在build/core/config.mk中有以下明確的定義:
Makefile代碼   收藏代碼
  1. CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk  
而BUILD_SYSTEM的定義在build/core/main.mk文件中:
BUILD_SYSTEM := $(TOPDIR)build/core
Makefile代碼   收藏代碼
  1. BUILD_SYSTEM := $(TOPDIR)build/core  
緊接着:
Makefile代碼   收藏代碼
  1. TOPDIR :=  
即TOPDIR爲android源碼的根目錄, BUILD_SYSTEM= build/core, 因此CLEAR_VARS變量的值就爲build/core/clear_vars.mk,固然這個也是相對於Android源碼根路徑。
獲得了CLEAR_VARS變量的值,咱們再回到my-dir函數中。
 
my-dir
根據gnu make定義,gnu make 會自動將全部讀取的makefile路徑都會加入到MAKEFILE_LIST變量中,並且是按照讀取的前後順序添加。
 
那麼,在運行本makefile文件時,$(MAKEFILE_LIST)字符串中最後一個makefile確定是最後讀取的makefile,即$(lastword $(MAKEFILE_LIST))則會返回build/tools/acp/Android.mk,此字符串通過$(eval md_file_ := $$(lastword $$(MAKEFILE_LIST))運算,賦值給了臨時變量md_file_。
 
判斷md_file_中是否包含CLEAR_VARS變量的值$(if $(filter $(CLEAR_VARS),$(md_file_)),確定也就會返回失敗,再經過$(patsubst %/,%,$(dir $(md_file_)))函數,則會獲得當前路徑,即build/tools/acp
 
若是咱們在include $(CLEAR_VARS)以後,再調用my-dir函數,那麼$$(lastword $$(MAKEFILE_LIST))確定就會返回$(BUILD_SYSTEM)/clear_vars.mk,同時,$(patsubst %/,%,$(dir $(md_file_))) 也就會返回$(BUILD_SYSTEM)的值build/core,而不是當前的路徑build/tools/acp。
 
這麼一來獲得的LOCAL_PATH的值就是錯誤的值,依賴LOCAL_PATH的其餘變量也就更加不多是正確的了!因此說 ,LOCAL_PATH必需要在任何including $(CLEAR_VARS))以前定義 。

討論完LOCAL_PATH,咱們緊接着來看看LOCAL_SRC_FILES。
LOCAL_SRC_FILES:
Makefile代碼   收藏代碼
  1. LOCAL_SRC_FILES := acp.c  
LOCAL_SRC_FILES變量的意思見名知意,很明顯是用來記錄當前模塊的源文件列表的一個變量。

這裏是他的賦值,咱們下面來看看他的使用的地方。在build/core/binary.mk中有以下的部分:
Makefile代碼   收藏代碼
  1. ###########################################################  
  2. ## C: Compile .c files to .o.  
  3. ###########################################################  
  4.   
  5. c_arm_sources    := $(patsubst %.c.arm,%.c,$(filter %.c.arm,$(LOCAL_SRC_FILES)))  
  6. c_arm_objects    := $(addprefix $(intermediates)/,$(c_arm_sources:.c=.o))  
  7.   
  8. c_normal_sources := $(filter %.c,$(LOCAL_SRC_FILES))  
  9. c_normal_objects := $(addprefix $(intermediates)/,$(c_normal_sources:.c=.o))  
  10.   
  11. $(c_arm_objects):    PRIVATE_ARM_MODE := $(arm_objects_mode)  
  12. $(c_arm_objects):    PRIVATE_ARM_CFLAGS := $(arm_objects_cflags)  
  13. $(c_normal_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)  
  14. $(c_normal_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)  
  15.   
  16. c_objects        := $(c_arm_objects) $(c_normal_objects)  
  17.   
  18. ifneq ($(strip $(c_objects)),)  
  19. $(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(proto_generated_headers) $(LOCAL_ADDITIONAL_DEPENDENCIES)  
  20.      $(transform-$(PRIVATE_HOST)c-to-o)  
  21. -include $(c_objects:%.o=%.P)  
  22. endif  
 
分析上面的代碼,
1. 咱們首先從LOCAL_SRC_FILES中獲得全部的C文件
     c_normal_sources value acp.c
2. 定義一個變量,c_normal_objects,用來表示生成的.o文件的路徑
     c_normal_objects value out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp.o
其中,$(c_normal_sources:.c=.o)會返回acp.o,那麼c_normal_objects的關鍵就是$(intermediates)變量

intermediates
經過查找,咱們能夠發如今如下地方有intermediates的賦值:
Makefile代碼   收藏代碼
  1. build/core/host_java_library.mk:intermediates := $(call local-intermediates-dir)  
  2. build/core/base_rules.mk:intermediates := $(call local-intermediates-dir)  
  3. build/core/dynamic_binary.mk:guessed_intermediates := $(call local-intermediates-dir)  
  4. build/core/java.mk:intermediates := $(call local-intermediates-dir)  
即,他們的都是調用local-intermediates-dir函數獲取當前的intermediates的值

在build/core/definitions.mk中有local-intermediates-dir函數的定義:
Makefile代碼   收藏代碼
  1. # Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE  
  2. # to determine the intermediates directory.  
  3. #  
  4. # $(1): if non-empty, force the intermediates to be COMMON  
  5. define local-intermediates-dir  
  6. $(strip \  
  7.     $(if $(strip $(LOCAL_MODULE_CLASS)),, \  
  8.         $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-intermediates-dir)) \  
  9.     $(if $(strip $(LOCAL_MODULE)),, \  
  10.         $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-intermediates-dir)) \  
  11.     $(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(LOCAL_IS_HOST_MODULE),$(1)) \  
  12. )  
  13. endef  
根據註釋,咱們知道 local-intermediates-dir的定義會依賴於LOCAL_MODULE_CLASS, LOCAL_MODULE, 和 LOCAL_IS_HOST_MODULE這三個變量的值。

咱們先經過添加打印的方式,獲得以下三個變量的值:
LOCAL_MODULE_CLASS value EXECUTABLES
LOCAL_MODULE value acp
LOCAL_IS_HOST_MODULE value true
其中,LOCAL_MODULE 的值是acp,這個確定的是沒問題的,由於在acp模塊的Android.mk文件中有明確的定義:LOCAL_MODULE := acp
那LOCAL_MODULE_CLASS 爲何是EXECUTABLES呢?咱們這裏先猜一下,估計是和最後一句include $(BUILD_HOST_EXECUTABLE )有關,
一樣,LOCAL_IS_HOST_MODULE 爲true,估計也是和include $(BUILD_HOST _EXECUTABLE)有關了。

咱們仍是先回到 local-intermediates-dir函數中,其中前兩個判斷是判斷是否認義LOCAL_MODULE_CLASS和LOCAL_MODULE,若是未空,則直接報錯,這裏也就說明了在每個編譯模塊中(即include $(CLEAR_VARS)和include $(BUILD_XXX)之間)必須定義LOCAL_MODULE的值。

所以,若是LOCAL_MODULE_CLASS和LOCAL_MODULE都不爲空時,則執行intermediates-dir-for這個函數,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(LOCAL_IS_HOST_MODULE)還有 local-intermediates-dir函數的第一個參數 $(1)會做爲參數傳給intermediates-dir-for函數。根據上面intermediates的賦值部分的代碼,咱們知道調用local-intermediates-dir函數時沒有傳遞任何參數,所以此時的$(1)即爲空,傳給intermediates-dir-for函數的也就只有上述的3個參數。

intermediates-dir-for
下面咱們來看intermediates-dir-for
Makefile代碼   收藏代碼
  1. ###########################################################  
  2. ## The intermediates directory.  Where object files go for  
  3. ## a given target.  We could technically get away without  
  4. ## the "_intermediates" suffix on the directory, but it's  
  5. ## nice to be able to grep for that string to find out if  
  6. ## anyone's abusing the system.  
  7. ###########################################################  
  8.   
  9. # $(1): target class, like "APPS"  
  10. # $(2): target name, like "NotePad"  
  11. # $(3): if non-empty, this is a HOST target.  
  12. # $(4): if non-empty, force the intermediates to be COMMON  
  13. define intermediates-dir-for  
  14. $(strip \  
  15.     $(eval _idfClass := $(strip $(1))) \  
  16.     $(if $(_idfClass),, \  
  17.         $(error $(LOCAL_PATH): Class not defined in call to intermediates-dir-for)) \  
  18.     $(eval _idfName := $(strip $(2))) \  
  19.     $(if $(_idfName),, \  
  20.         $(error $(LOCAL_PATH): Name not defined in call to intermediates-dir-for)) \  
  21.     $(eval _idfPrefix := $(if $(strip $(3)),HOST,TARGET)) \  
  22.     $(if $(filter $(_idfPrefix)-$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \  
  23.         $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_INTERMEDIATES)) \  
  24.       , \  
  25.         $(eval _idfIntBase := $($(_idfPrefix)_OUT_INTERMEDIATES)) \  
  26.      ) \  
  27.     $(_idfIntBase)/$(_idfClass)/$(_idfName)_intermediates \  
  28. )  
  29. endef  
根據註釋,咱們能夠知道$(1),$(2),$(3),$(4)這4個參數的意義。
而如下三行則是使用$(1),$(2),$(3)來定義三個臨時變量:_idfClass,_idfName 和_idfPrefix 
$(eval _idfClass := $(strip $(1))) 
$(eval _idfName := $(strip $(2)))
$(eval _idfPrefix := $(if $(strip $(3)),HOST,TARGET))
在本例中這三個臨時變量的值則爲:EXECUTABLES、acp和HOST

下面關鍵的一個臨時變量是_idfIntBase ,咱們發現不管$(if $(filter $(_idfPrefix)-$(_idfClass),$(COMMON_MODULE_CLASSES))$(4)是真仍是假,
_idfIntBase 的值不是$(HOST_OUT_COMMON_INTERMEDIATES)就是$(HOST_OUT_INTERMEDIATES),由於上面調用時$(4)爲空,在這裏估計判斷結果 應該爲假,即應該執行
Makefile代碼   收藏代碼
  1. $(eval _idfIntBase := $($(_idfPrefix)_OUT_INTERMEDIATES))  

下面我就看看這個$(HOST_OUT_INTERMEDIATES )究竟是一個什麼變量。
在build/core/envsetup.mk中有明確的定義:
Makefile代碼   收藏代碼
  1. HOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj   
HOST_OUT也在本文件中定義:
Makefile代碼   收藏代碼
  1. HOST_OUT := $(HOST_OUT_$(HOST_BUILD_TYPE))   
HOST_BUILD_TYPE默認值爲release:
Makefile代碼   收藏代碼
  1. # the host build defaults to release, and it must be release or debug  
  2. ifeq ($(HOST_BUILD_TYPE),)  
  3. HOST_BUILD_TYPE := release  
  4. endif   
所以,
Makefile代碼   收藏代碼
  1. HOST_OUT := $(HOST_OUT_release)  
而HOST_OUT_release的定義以下:
Makefile代碼   收藏代碼
  1. HOST_OUT_release := $(HOST_OUT_ROOT_release)/$(HOST_OS)-$(HOST_ARCH)  
HOST_OUT_ROOT_release的定義:
Makefile代碼   收藏代碼
  1. HOST_OUT_ROOT_release := $(OUT_DIR)/host   
在Linux上編譯,所以HOST_OS := linux ,而咱們的機器採用的是Intel Xeon X3440 CPU,即x86架構,所以HOST_ARCH:= x86

通過上述分析,
$(HOST_OUT_INTERMEDIATES ) =out/host/linux-x86/obj
intermediates-dir-for 函數返回out/host/linux-x86/ob/EXECUTABLES/acp_intermediates
local-intermediates-dir 函數返回out/host/linux-x86/ob/EXECUTABLES/acp_intermediates
intermediates 變量的值爲out/host/linux-x86/ob/EXECUTABLES/acp_intermediates
c_normal_objects 變量的值爲out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp.o

c_objects
下面咱們回到LOCAL_SRC_FILES的編譯部分
Makefile代碼   收藏代碼
  1.  c_objects        := $(c_arm_objects) $(c_normal_objects)  
  2.   
  3. ifneq ($(strip $(c_objects)),)  
  4. $(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(proto_generated_headers) $(LOCAL_ADDITIONAL_DEPENDENCIES)  
  5.      $(transform-$(PRIVATE_HOST)c-to-o)  
  6. -include $(c_objects:%.o=%.P)  
  7. endif   
其中c_arm_objects爲空,c_normal_objects 的值爲out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp.o
因此c_objects 的值也爲out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp.o

執行c_objects目標時,依賴$(intermediates)/%.o、 $(TOPDIR)$(LOCAL_PATH)/%.c、$(yacc_cpps) 、$(proto_generated_headers)和$(LOCAL_ADDITIONAL_DEPENDENCIES)
執行以下操做: $(transform-$(PRIVATE_HOST)c-to-o)
相關文章
相關標籤/搜索