【轉】Android源代碼編譯命令m/mm/mmm/make分析--不錯


原文網址:http://blog.csdn.net/luoshengyang/article/details/19023609

在前文中,我們分析了Android編譯環境的初始化過程。Android編譯環境初始化完成后,我們就可以用m/mm/mmm/make命令編譯源代碼了。當然,這要求每一個模塊都有一個Android.mk文件。Android.mk實際上是一個Makefile腳本,用來描述模塊編譯信息。Android編譯系統通過整合Android.mk文件完成編譯過程。本文就對Android源代碼的編譯過程進行詳細分析。

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!

        從前面Android編譯系統環境初始化過程分析這篇文章可以知道,lunch命令其實是定義在build/envsetup.sh文件中的函數lunch提供的。與lunch命令一樣,m、mm和mmm命令也分別是由定義在build/envsetup.sh文件中的函數m、mm和mmm提供的,而這三個函數又都是通過make命令來對源代碼進行編譯的。事實上,命令m就是對make命令的簡單封裝,並且是用來對整個Android源代碼進行編譯,而命令mm和mmm都是通過make命令來對Android源碼中的指定模塊進行編譯。接下來我們就先分別介紹一下函數m、mm和mmm的實現,然后進一步分析它們是如何通過make命令來編譯代碼的。

        函數m的實現如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. function m()  
  2. {  
  3.     T=$(gettop)  
  4.     if [ "$T" ]; then  
  5.         make -C $T $@  
  6.     else  
  7.         echo "Couldn't locate the top of the tree.  Try setting TOP."  
  8.     fi  
  9. }  

       函數m調用函數gettop得到的是Android源代碼根目錄T。在執行make命令的時候,先通過-C選項指定工作目錄為T,即Android源代碼根目錄,接着又將執行命令m指定的參數$@作為命令make的參數。從這里就可以看出,命令m實際上就是對命令make的簡單封裝。

 

       函數mm的實現如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. function mm()  
  2. {  
  3.     # If we're sitting in the root of the build tree, just do a  
  4.     # normal make.  
  5.     if [ -f build/core/envsetup.mk -a -f Makefile ]; then  
  6.         make $@  
  7.     else  
  8.         # Find the closest Android.mk file.  
  9.         T=$(gettop)  
  10.         local M=$(findmakefile)  
  11.         # Remove the path to top as the makefilepath needs to be relative  
  12.         local M=`echo $M|sed 's:'$T'/::'`  
  13.         if [ ! "$T" ]; then  
  14.             echo "Couldn't locate the top of the tree.  Try setting TOP."  
  15.         elif [ ! "$M" ]; then  
  16.             echo "Couldn't locate a makefile from the current directory."  
  17.         else  
  18.             ONE_SHOT_MAKEFILE=$M make -C $T all_modules $@  
  19.         fi  
  20.     fi  
  21. }  

        函數mm首先是判斷當前目錄是否就是Android源碼根目錄,即當前目錄下是否存在一個build/core/envsetup.mk文件和一個Makefile文件。如果是的話,就將命令mm當作是一個普通的make命令來執行。否則的話,就調用函數findmakefile從當前目錄開始一直往上尋找是否存在一個Android.mk文件。如果在尋找的過程中,發現了一個Android.mk文件,那么就獲得它的絕對路徑,並且停止上述尋找過程。

 

        由於接下來執行make命令時,我們需要指定的是要編譯的Android.mk文件的相對於Android源碼根目錄路徑,因此函數mm需要將剛才找到的Android.mk絕對文件路徑M中與Android源碼根目錄T相同的那部分路徑去掉。這是通過sed命令來實現的,也就是將字符串M前面與字符串T相同的子串刪掉。

        最后,將找到的Android.mk文件的相對路徑設置給環境變量ONE_SHOT_MAKE,表示接下來要對它進行編譯。另外,函數mm還將make命令目標設置為all_modules。這是什么意思呢?我們知道,一個Android.mk文件同時可以定義多個模塊,因此,all_modules就表示要對前面指定的Android.mk文件中定義的所有模塊進行編譯。

         函數mmm的實現如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. function mmm()  
  2. {  
  3.     T=$(gettop)  
  4.     if [ "$T" ]; then  
  5.         local MAKEFILE=  
  6.         local MODULES=  
  7.         local ARGS=  
  8.         local DIR TO_CHOP  
  9.         local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/')  
  10.         local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/')  
  11.         for DIR in $DIRS ; do  
  12.             MODULES=`echo $DIR | sed -n -e 's/.*:.$/\1/p' | sed 's/,/ /'`  
  13.             if [ "$MODULES" = "" ]; then  
  14.                 MODULES=all_modules  
  15.             fi  
  16.             DIR=`echo $DIR | sed -e 's/:.*//' -e 's:/$::'`  
  17.             if [ -f $DIR/Android.mk ]; then  
  18.                 TO_CHOP=`(cd -P -- $T && pwd -P) | wc -c | tr -d ' '`  
  19.                 TO_CHOP=`expr $TO_CHOP + 1`  
  20.                 START=`PWD= /bin/pwd`  
  21.                 MFILE=`echo $START | cut -c${TO_CHOP}-`  
  22.                 if [ "$MFILE" = "" ] ; then  
  23.                     MFILE=$DIR/Android.mk  
  24.                 else  
  25.                     MFILE=$MFILE/$DIR/Android.mk  
  26.                 fi  
  27.                 MAKEFILE="$MAKEFILE $MFILE"  
  28.             else  
  29.                 if [ "$DIR" = snod ]; then  
  30.                     ARGS="$ARGS snod"  
  31.                 elif [ "$DIR" = showcommands ]; then  
  32.                     ARGS="$ARGS showcommands"  
  33.                 elif [ "$DIR" = dist ]; then  
  34.                     ARGS="$ARGS dist"  
  35.                 elif [ "$DIR" = incrementaljavac ]; then  
  36.                     ARGS="$ARGS incrementaljavac"  
  37.                 else  
  38.                     echo "No Android.mk in $DIR."  
  39.                     return 1  
  40.                 fi  
  41.             fi  
  42.         done  
  43.         ONE_SHOT_MAKEFILE="$MAKEFILE" make -C $T $DASH_ARGS $MODULES $ARGS  
  44.     else  
  45.         echo "Couldn't locate the top of the tree.  Try setting TOP."  
  46.     fi  
  47. }  

        函數mmm的實現就稍微復雜一點,我們詳細解釋一下。

 

        首先,命令mmm可以這樣執行:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. $ mmm <dir-1> <dir-2> ... <dir-N>[:module-1,module-2,...,module-M]    

        其中,dir-1、dir-2、dir-N都是包含有Android.mk文件的目錄。在最后一個目錄dir-N的后面可以帶一個冒號,冒號后面可以通過逗號分隔一系列的模塊名稱module-1、module-2和module-M,用來表示要編譯前面指定的Android.mk中的哪些模塊。

 

        知道了命令mmm的使用方法之后 ,我們就可以分析函數mmm的執行邏輯了:

        1. 調用函數gettop獲得Android源碼根目錄。

        2. 通過命令awk將執行命令mmm時指定的選項參數提取出來,也就是將以橫線“-”開頭的字符串提取出來,並且保存在變量DASH_ARGS中。

        3. 通過命令awk將執行命令mmm時指定的非選項參數提取出來,也就是將非以橫線“-”開頭的字符串提取出來,並且保存在變量DIRS中。這里得到的實際上就是跟在命令mmm后面的字符串“<dir-1> <dir-2> ... <dir-N>[:module-1,module-2,...,module-M]”。

        4. 變量DIRS保存的字符串可以看成是一系以空格分隔的子字符串,因此,就可以通過一個for循環來對這些子字府串進行遍歷。每一個子字符串DIR描述的都是一個包含有Android.mk文件的目錄。對每一個目錄DIR執行以下操作:

        4.1 由於目錄DIR后面可能會通過冒號指定有模塊名稱,因此就先通過兩個sed命令來獲得這些模塊名稱。第一個sed命令獲得的是一系列以逗號分隔的模塊名稱列表,第二個sed命令用來將前面獲得的以逗號分隔的模塊名稱列表轉化為以空格分隔的模塊名稱列表。最后,獲得的以空格分隔的模塊名稱列表保存在變量MODULES中。由於目錄DIR后面也可能不指定有模塊名稱,因此前面得到的變量MODULES的值就會為空。在這種情況下,需要將變量MODULES的值設置為“all_modules”,表示要編譯的是所有模塊。

        4.2 通過兩個sed命令獲得真正的目錄DIR。第一個sed命令將原來DIR字符串后面的冒號以及冒號后面的模塊列表字符串刪掉。第二個sed命令將執行前面一個sed命令獲得的目錄后面的"/"斜線去掉,最后就得到一個末尾不帶有斜線“/”的路徑,並且保存在變量DIR中。

        4.3 如果變量DIR描述的是一個真正的路徑,也就是在該路徑下存在一個Android.mk文件,那么就進行以下處理:

        4.3.1 統計Android源碼根目錄T包含的字符數,並且將這個字符數加1,得到的值保存在變量TO_CHOP中。

        4.3.2 通過執行/bin/pwd命令獲得當前執行命令mmm的目錄START。

        4.3.3 通過cut命令獲得當前目錄START相對於Android源碼根目錄T的路徑,並且保存在變量MFILE中。

        4.3.4 如果變量MFILE的值等於空,就表明是在Android源碼根目錄T中執行mmm命令,這時候就表明變量DIR描述的就是相對Android源碼根目錄T的一個目錄,這時候指定的Android.mk文件相對於Android源碼根目錄T的路徑就為$DIR/Android.mk。

        4.3.5 如果變量MFILE的值不等於空,就表明是在Android源碼根目錄T的某一個子目錄中執行mmm命令,這時候$MFILE/$DIR/Android.mk表示的Android.mk文件路徑才是相對於Android源碼根目錄T的。

        4.3.6 將獲得的Android.mk路徑MFILE附加在變量MAKEFILE描述的字符串的后面,並且以空格分隔。

        4.4 如果變量DIR描述的不是一個真正的路徑,並且它的值等於"snod"、"showcomands"、“dist”或者“incrementaljavac”,那么它描述的其實是make修飾命令。這四個修飾命令的含義分別如下所示:

        4.4.1 snod是“systemimage with no dependencies”的意思,表示忽略依賴性地重新打包system.img。

        4.4.2 showcommands表示顯示編譯過程中執行的命令。

        4.4.3 dist表示將編譯后產生的發布文件拷貝到out/dist目錄中。

        4.4.4 incrementaljavac表示對Java源文件采用增量式編譯,也就是如果一個Java文件如果沒有修改過,那么就不要重新生成對應的class文件。

        5. 上面的for循環執行完畢,變量MAKEFILE保存的是要編譯的Android.mk文件列表,它們都是相對於Android源碼根目錄的路徑,變量DASH_ARGS保存的是原來執行mmm命令時帶的選項參數,變量MODULES保存的是指定要編譯的模塊名稱,變量ARGS保存的是修飾命令。其中,變量MAKEFILE的內容通過環境變量ONE_SHOT_MAKEFILE傳遞給make命令,而其余變量都是通過參數的形式傳遞給make命令,並且變量MODULES作為make命令的目標。

        明白了函數m、mm和mmm的實現之后,我們就可以知道:

        1. mm和mmm命令是類似的,它們都是用來編譯某些模塊。

        2. m命令用來編譯所有模塊。

        如果我們理解了mm或者mmm命令的編譯過程,那么自然也會明白m命令的編譯過程,因為所有模塊的編譯過程就等於把每一個模塊的編譯都編譯出來,因此,接下來我們就選擇具有代表性的、常用的編譯命令mmm來分析Android源碼的編譯過程,如圖1所示:

圖1 mmm命令的編譯過程

       函數mmm在Android源碼根目錄執行make命令的時候,沒有通過-f指定Makefile文件,因此默認就使用Android源碼根目錄下的Makefile文件,它的內容如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. ### DO NOT EDIT THIS FILE ###  
  2. include build/core/main.mk  
  3. ### DO NOT EDIT THIS FILE ###  

       它僅僅是將build/core/main.mk文件加載進來。build/core/main.mk是Android編譯系統的入口文件,它通過加載其它的mk文件來對Android源碼中的各個模塊進行編譯,以及將編譯出來的文件打包成各種鏡像文件。以下就是build/core/main.mk文件的主要內容:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. ......  
  2.   
  3. # This is the default target.  It must be the first declared target.  
  4. .PHONY: droid  
  5. DEFAULT_GOAL := droid  
  6. $(DEFAULT_GOAL):  
  7. ......  
  8.   
  9. # Set up various standard variables based on configuration  
  10. # and host information.  
  11. include $(BUILD_SYSTEM)/config.mk  
  12. ......  
  13.   
  14. # Bring in standard build system definitions.  
  15. include $(BUILD_SYSTEM)/definitions.mk  
  16. ......  
  17.   
  18. # These targets are going to delete stuff, don't bother including  
  19. # the whole directory tree if that's all we're going to do  
  20. ifeq ($(MAKECMDGOALS),clean)  
  21. dont_bother := true  
  22. endif  
  23. ifeq ($(MAKECMDGOALS),clobber)  
  24. dont_bother := true  
  25. endif  
  26. ifeq ($(MAKECMDGOALS),dataclean)  
  27. dont_bother := true  
  28. endif  
  29. ifeq ($(MAKECMDGOALS),installclean)  
  30. dont_bother := true  
  31. endif  
  32.   
  33. # Bring in all modules that need to be built.  
  34. ifneq ($(dont_bother),true)  
  35.   
  36. ......  
  37.   
  38. ifneq ($(ONE_SHOT_MAKEFILE),)  
  39. # We've probably been invoked by the "mm" shell function  
  40. # with a subdirectory's makefile.  
  41. include $(ONE_SHOT_MAKEFILE)  
  42. ......  
  43. else # ONE_SHOT_MAKEFILE  
  44.   
  45. #  
  46. # Include all of the makefiles in the system  
  47. #  
  48.   
  49. # Can't use first-makefiles-under here because  
  50. # --mindepth=2 makes the prunes not work.  
  51. subdir_makefiles := \  
  52.     $(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk)  
  53.   
  54. include $(subdir_makefiles)  
  55.   
  56. endif # ONE_SHOT_MAKEFILE  
  57.   
  58. ......  
  59.   
  60. # -------------------------------------------------------------------  
  61. # Define dependencies for modules that require other modules.  
  62. # This can only happen now, after we've read in all module makefiles.  
  63. #  
  64. # TODO: deal with the fact that a bare module name isn't  
  65. # unambiguous enough.  Maybe declare short targets like  
  66. # APPS:Quake or HOST:SHARED_LIBRARIES:libutils.  
  67. # BUG: the system image won't know to depend on modules that are  
  68. # brought in as requirements of other modules.  
  69. define add-required-deps  
  70. $(1): $(2)  
  71. endef  
  72. $(foreach m,$(ALL_MODULES), \  
  73.   $(eval r := $(ALL_MODULES.$(m).REQUIRED)) \  
  74.   $(if $(r), \  
  75.     $(eval r := $(call module-installed-files,$(r))) \  
  76.     $(eval $(call add-required-deps,$(ALL_MODULES.$(m).INSTALLED),$(r))) \  
  77.    ) \  
  78.  )  
  79. ......  
  80.   
  81. modules_to_install := $(sort \  
  82.     $(ALL_DEFAULT_INSTALLED_MODULES) \  
  83.     $(product_FILES) \  
  84.     $(foreach tag,$(tags_to_install),$($(tag)_MODULES)) \  
  85.     $(call get-tagged-modules, shell_$(TARGET_SHELL)) \  
  86.     $(CUSTOM_MODULES) \  
  87.   )  
  88. ......  
  89.   
  90. # build/core/Makefile contains extra stuff that we don't want to pollute this  
  91. # top-level makefile with.  It expects that ALL_DEFAULT_INSTALLED_MODULES  
  92. # contains everything that's built during the current make, but it also further  
  93. # extends ALL_DEFAULT_INSTALLED_MODULES.  
  94. ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)  
  95. include $(BUILD_SYSTEM)/Makefile  
  96. modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))  
  97. ALL_DEFAULT_INSTALLED_MODULES :=  
  98.   
  99. endif # dont_bother  
  100.   
  101. ......  
  102.   
  103. # -------------------------------------------------------------------  
  104. # This is used to to get the ordering right, you can also use these,  
  105. # but they're considered undocumented, so don't complain if their  
  106. # behavior changes.  
  107. .PHONY: prebuilt  
  108. prebuilt: $(ALL_PREBUILT)  
  109. ......  
  110.   
  111. # All the droid stuff, in directories  
  112. .PHONY: files  
  113. files: prebuilt \  
  114.         $(modules_to_install) \  
  115.         $(modules_to_check) \  
  116.         $(INSTALLED_ANDROID_INFO_TXT_TARGET)  
  117. ......  
  118.   
  119. # Build files and then package it into the rom formats  
  120. .PHONY: droidcore  
  121. droidcore: files \  
  122.     systemimage \  
  123.     $(INSTALLED_BOOTIMAGE_TARGET) \  
  124.     $(INSTALLED_RECOVERYIMAGE_TARGET) \  
  125.     $(INSTALLED_USERDATAIMAGE_TARGET) \  
  126.     $(INSTALLED_CACHEIMAGE_TARGET) \  
  127.     $(INSTALLED_FILES_FILE)  
  128.   
  129. ......  
  130.   
  131. # Dist for droid if droid is among the cmd goals, or no cmd goal is given.  
  132. ifneq ($(filter droid,$(MAKECMDGOALS))$(filter ||,|$(filter-out $(INTERNAL_MODIFIER_TARGETS),$(MAKECMDGOALS))|),)  
  133.   
  134. ifneq ($(TARGET_BUILD_APPS),)  
  135.   # If this build is just for apps, only build apps and not the full system by default.  
  136. ......  
  137.   
  138. .PHONY: apps_only  
  139. apps_only: $(unbundled_build_modules)  
  140.   
  141. droid: apps_only  
  142.   
  143. else # TARGET_BUILD_APPS  
  144. ......  
  145.   
  146. # Building a full system-- the default is to build droidcore  
  147. droid: droidcore dist_files  
  148.   
  149. endif # TARGET_BUILD_APPS  
  150. endif # droid in $(MAKECMDGOALS)  
  151. ......  
  152.   
  153. # phony target that include any targets in $(ALL_MODULES)  
  154. .PHONY: all_modules  
  155. all_modules: $(ALL_MODULES)  
  156.   
  157. ......  

       接下來我們就先對build/core/main.mk文件的核心邏輯進行分析,然后再進一步對其中涉及到的關鍵點進行分析。

 

        build/core/main.mk文件的執行過程如下所示:

        1. 定義默認make目標為droid。目標droid根據不同的情形有不同的依賴關系。如果在初始化編譯環境時,指定了TARGET_BUILD_APPS環境變量,那么就表示當前只編譯特定的模塊,這些特定的模塊保存在變量unbundled_build_modules中,這時候目標droid就透過另外一個偽目標app_only依賴它們。如果在初始化編譯環境時沒有指定TARGET_BUILD_APPS環境變量,那么目標droid就依賴於另外兩個文件droidcore和dist_files。droidcore是一個make偽目標,它依賴於各種預編譯文件,以及system.img、boot.img、recovery.img和userdata.img等鏡像文件。dist_files也是一個make偽目標,用來指定一些需要在編譯后拷貝到out/dist目錄的文件。也就是說,當我們在Android源碼目錄中執行不帶目標的make命令時,默認就會對目標droid進行編譯,也就是會將整個Android系統編譯出來。

        2. 加載build/core/config.mk文件。從前面Android編譯系統環境初始化過程分析一文可以知道,在加載build/core/config.mk文件的過程中,會在執行make命令的進程中完成對Android編譯環境的初始化過程,也就是會指定好目標設備以及編譯類型。

        3. 加載build/croe/definitions.mk文件。該文件定義了很多在編譯過程中要用到的宏,相當於就是定義了很多通用函數,供編譯過程調用。

        4. 如果在執行make命令時,指定的不是清理文件相關的目標,也就是不是clean、clobber、dataclean和installclean等目標,那么就會將變量dont_bother的值設置為true,表示接下來要執行的是編譯命令。

        5. 在變量dont_bother的值等於true的情況下,如果環境變量ONE_SHOT_MAKEFILE的值不等於空,也就是我們執行的是mm或者mmm命令,那么就表示要編譯的是特定的模塊。這些指定要編譯的模塊的Android.mk文件路徑就保存在環境變量ONE_SHOT_MAKEFILE中,因此直接將這些Android,mk文件加載進來就獲得相應的編譯規則。另一方面,如果環境變量ONE_SHOT_MAKEFILE的值等於空,那么就說明我們執行的是m或者make命令,那么就表示要對Android源代碼中的所有模塊進行編譯,這時候就通過build/tools/findleaves.py腳本獲得Android源代碼工程下的所有Android.mk文件的路徑列表,並且將這些Android.mk文件加載進行獲得相應的編譯規則。

        6. 上一步指定的Android.mk文件加載完成之后,變量ALL_MODULES就包含了所有要編譯的模塊的名稱,這些模塊名稱以空格來分隔形成成一個列表。

        7. 生成模塊依賴規則。每一個模塊都可以通過LOCAL_REQUIRED_MODULES來指定它所依賴的其它模塊,也就是說當一個模塊被安裝時,它所依賴的其它模塊也同樣會被安裝。每一個模塊m依賴的所有模塊都會被保存在ALL_MODULES.$(m).REQUIRED變量中。對於每一個被依賴模塊r,我們需要獲得它的安裝文件,也就是最終生成的模塊文件的文件路徑,以便可以生成相應的編譯規則。獲得一個模塊m的安裝文件是通過調用函數module-installed-files來實現的,實質上就是保存在$(ALL_MODULES.$(m).INSTALLED變量中。知道了一個模塊m的所依賴的模塊的安裝文件路徑之后,我們就可以通過函數add-required-deps來指定它們之間的依賴關系了。注意,這里實際上指定的是模塊m的安裝文件與它所依賴的模塊r的安裝文件的依賴關系。

        8. 將所有要安裝的模塊都保存在變量ALL_DEFAULT_INSTALLED_MODULES中,並且將build/core/Makefie文件加載進來。 build/core/Makefie文件會根據要安裝的模塊產成system.img、boot.img和recovery.img等鏡像文件的生成規則。

        9. 前面提到,當執行mm命令時,make目標指定為all_moudles。另外,當執行mmm命令時,默認的make目標也指定為all_moudles。因此,我們需要指定目標all_modules的編譯規則,實際上只要將它依賴於當前要編譯的所有模塊就行了,也就是依賴於由變量ALL_MODULES所描述的模塊。

       在上述過程中,最核心的就是第5步和第8步。由於本文只關心Android源碼的編譯過程,因此我們只分析第5步的執行過程。在接下來一篇文章中分析Android鏡像文件的生成過程時,我們再分析第8步的執行過程。

       第5步實際上就是將指定模塊的Android.mk文件加載進來。一個典型的Android.mk文件如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. LOCAL_PATH:= $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. LOCAL_MODULE_TAGS := optional  
  5. LOCAL_MODULE    := libdis  
  6.   
  7. LOCAL_SHARED_LIBRARIES := \  
  8.     liblog \  
  9.     libdl  
  10.   
  11. LOCAL_SRC_FILES := \  
  12.     dispatcher.cpp \  
  13.     ../common/common.cpp  
  14.   
  15. include $(BUILD_SHARED_LIBRARY)  

       以LOCAL開頭的變量都是屬於模塊局部變量,也就是說,一個模塊在開始編譯之前,必須要先對它們進行清理,然后再進行初始化。Android編譯系統定義了非常多的模塊局部變量,因此我們不可能手動地一個一個清理,需要加載一個由變量CLEAR_VARS指定的Makefile腳本來幫我們自動清理。變量CLEAR_VARS的值定義在build/core/config.mk文件,它的值等於build/core/clear_vars.mk。

 

       Android.mk文件中還有一個重要的變量LOCAL_PATH,用來指定當前正在編譯的模塊的目錄,我們可以通過調用宏my-dir來獲得。宏my-dir定義在build/core/definitions.mk文件,它實際上就是將當前正在加載的Android.mk文件路徑的目錄名提取出來。

       Android.mk文件接下來就是通過其它的LOCAL變量定義模塊名稱、源文件,以及所要依賴的各種庫文件等等。例如,在我們這個例子,模塊名稱定義為libdis,參與編譯的源文件為dispatcher.cpp和common.cpp、依賴的庫文件為liblog和libdl。

       最后,Android文件通過加載一個模板文件來告訴編譯系統它所要編譯的模塊的類型。例如,在我們這個例子中,就是通過加載由變量BUILD_SHARED_LIBRARY指定的模板文件來告訴編譯系統我們要編譯的模塊是一個動態鏈接庫。變量BUILD_SHARED_LIBRARY的值定義在build/core/config.mk文件,它的值等於build/core/shared_library.mk。

       Android編譯系統定義了非常多的模板文件,每一個模板文件都對應一種類型的模塊,例如除了我們上面的動態鏈接庫模板文件之外,還有:

       BUILD_PACKAGE:指向build/core/package.mk,用來編譯APK文件。

       BUILD_JAVA_LIBRARY:指向build/core/java_library.mk,用來編譯Java庫文件。

       BUILD_STATIC_JAVA_LIBRARY:指向build/core/tatic_java_library.mk,用來編譯Java靜態庫文件。

       BUILD_STATIC_LIBRARY:指向build/core/static_library.mk,用來編譯靜態庫文件。也就是.a文件。

       BUILD_EXECUTABLE:指向build/core/executable.mk,用來編譯可執行文件。

       BUILD_PREBUILT:指向build/core/prebuilt.mk。用來編譯已經預編譯好的第三方庫文件,實際上是將這些預編譯好的第三方庫文件拷貝到合適的位置去,以便可以讓其它模塊引用。

       不管編譯何種類型的模塊,都是主要完成以下的工作:

       1. 制定好相應的依賴規則

       2. 調用合適的命令進行編譯

       為了簡單起見,接下來我們就以動態鏈接庫(即.so文件)的編譯過程為例來說明Android編譯命令mmm的執行過程。

       在分析動態鏈接庫的編譯過程之前,我們首先看一看使用mmm命令來編譯上述的Android.mk文件時得到的輸出,如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. target thumb C++: libdis <= external/si/dispatcher/dispatcher.cpp  
  2. target thumb C++: libdis <= external/si/dispatcher/../common/common.cpp  
  3. target SharedLib: libdis (out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so)  
  4. target Symbolic: libdis (out/target/product/generic/symbols/system/lib/libdis.so)  
  5. target Strip: libdis (out/target/product/generic/obj/lib/libdis.so)  
  6. Install: out/target/product/generic/system/lib/libdis.so  

       從這些輸出我們大體推斷出一些文件之間的依賴關系及其生成過程:

 

       1. out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so文件依賴於external/si/dispatcher/dispatcher.cpp和external/si/dispatcher/../common/common.cpp文件,並且由它們生成。

       2. out/target/product/generic/symbols/system/lib/libdis.so依賴於out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so文件,並且由它生成。

       3. out/target/product/generic/obj/lib/libdis.so依賴於out/target/product/generic/symbols/system/lib/libdis.so文件,並且由它生成。

       4. out/target/product/generic/system/lib/libdis.so依賴於out/target/product/generic/obj/lib/libdis.so文件,並且由它生成。

       回憶前面的分析,我們提到,當執行mmm命令時,默認的make目標是all_modules,並且它依賴於變量ALL_MODULES指向的文件或者目標,因此,我們可以繼續推斷出,變量ALL_MODULES指向的文件或者目標一定會與文件out/target/product/generic/system/lib/libdis.so有依賴關系,這樣才能夠從make目標all_modules開始鏈式地生成上述文件。在接下來的分析中,我們就按照抓住上述文件的依賴關系進行逆向分析。

      從上面的分析可以知道,在編譯動態鏈接庫文件的過程中,文件build/core/shared_library.mk會被加載,它的核心內容如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. ......  
  2.   
  3. ifeq ($(strip $(LOCAL_MODULE_CLASS)),)  
  4. LOCAL_MODULE_CLASS := SHARED_LIBRARIES  
  5. endif  
  6. ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)  
  7. LOCAL_MODULE_SUFFIX := $(TARGET_SHLIB_SUFFIX)  
  8. endif  
  9.   
  10. ......  
  11.   
  12. include $(BUILD_SYSTEM)/dynamic_binary.mk  
  13.   
  14. ......  
  15.   
  16. $(linked_module): $(all_objects) $(all_libraries) \  
  17.                   $(LOCAL_ADDITIONAL_DEPENDENCIES) \  
  18.                   $(my_target_crtbegin_so_o) $(my_target_crtend_so_o)  
  19.     $(transform-o-to-shared-lib)  

       LOCAL_MODULE_CLASS用來描述模塊文件的類型。對於動態鏈接庫文件來說,如果我們沒有對它進行設置的話,它的默認值就等於SHARED_LIBRARIES。

 

       LOCAL_MODULE_SUFFIX用來描述生成的模塊文件的后綴名。對於動態鏈接庫文件來說,如果我們沒有對它進行設置的話,它的默認值就等於TARGET_SHLIB_SUFFIX。即.so。

       上述兩個變量限定了生成的動態鏈接庫文件的完整文件名以及保存位置。

       接下來,build/core/shared_library.mk文件加載了另外一個文件build/core/dynamic_binary.mk文件,並且為變量linked_module指向的文件制定了一個依賴規則,這個依賴規則由函數transform-o-to-shared-lib來執行。從函數transform-o-to-shared-lib就可以知道,它是根據一系列的中間編譯文件(object文件)以及依賴庫文件生成指定的動態鏈庫文件的,主要就是由變量all_objects和all_libraries所描述的文件。現在,變量linked_module、all_objects和all_libraries所指向的文件是我們所要關心的。

       我們接着分析文件build/core/dynamic_binary.mk文件的加載過程,它的內容如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. ......  
  2.   
  3. LOCAL_UNSTRIPPED_PATH := $(strip $(LOCAL_UNSTRIPPED_PATH))  
  4. ifeq ($(LOCAL_UNSTRIPPED_PATH),)  
  5.   ifeq ($(LOCAL_MODULE_PATH),)  
  6.     LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_$(LOCAL_MODULE_CLASS)_UNSTRIPPED)  
  7.   else  
  8.     # We have to figure out the corresponding unstripped path if LOCAL_MODULE_PATH is customized.  
  9.     LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_UNSTRIPPED)/$(patsubst $(PRODUCT_OUT)/%,%,$(LOCAL_MODULE_PATH))  
  10.   endif  
  11. endif  
  12.   
  13. LOCAL_MODULE_STEM := $(strip $(LOCAL_MODULE_STEM))  
  14. ifeq ($(LOCAL_MODULE_STEM),)  
  15.   LOCAL_MODULE_STEM := $(LOCAL_MODULE)  
  16. endif  
  17. LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE_STEM)$(LOCAL_MODULE_SUFFIX)  
  18. LOCAL_BUILT_MODULE_STEM := $(LOCAL_INSTALLED_MODULE_STEM)  
  19.   
  20. # base_rules.make defines $(intermediates), but we need its value  
  21. # before we include base_rules.  Make a guess, and verify that  
  22. # it's correct once the real value is defined.  
  23. guessed_intermediates := $(call local-intermediates-dir)  
  24.   
  25. ......  
  26.   
  27. linked_module := $(guessed_intermediates)/LINKED/$(LOCAL_BUILT_MODULE_STEM)  
  28.   
  29. ......  
  30.   
  31. LOCAL_INTERMEDIATE_TARGETS := $(linked_module)  
  32.   
  33. ###################################  
  34. include $(BUILD_SYSTEM)/binary.mk  
  35. ###################################  
  36.   
  37. ......  
  38.   
  39. ###########################################################  
  40. ## Compress  
  41. ###########################################################  
  42. compress_input := $(linked_module)  
  43.   
  44. ifeq ($(strip $(LOCAL_COMPRESS_MODULE_SYMBOLS)),)  
  45.   LOCAL_COMPRESS_MODULE_SYMBOLS := $(strip $(TARGET_COMPRESS_MODULE_SYMBOLS))  
  46. endif  
  47.   
  48. ifeq ($(LOCAL_COMPRESS_MODULE_SYMBOLS),true)  
  49. $(error Symbol compression not yet supported.)  
  50. compress_output := $(intermediates)/COMPRESSED-$(LOCAL_BUILT_MODULE_STEM)  
  51.   
  52. #TODO: write the real $(STRIPPER) rule.  
  53. #TODO: define a rule to build TARGET_SYMBOL_FILTER_FILE, and  
  54. #      make it depend on ALL_ORIGINAL_DYNAMIC_BINARIES.  
  55. $(compress_output): $(compress_input) $(TARGET_SYMBOL_FILTER_FILE) | $(ACP)  
  56.     @echo "target Compress Symbols: $(PRIVATE_MODULE) ($@)"  
  57.     $(copy-file-to-target)  
  58. else  
  59. # Skip this step.  
  60. compress_output := $(compress_input)  
  61. endif  
  62.   
  63. ###########################################################  
  64. ## Store a copy with symbols for symbolic debugging  
  65. ###########################################################  
  66. symbolic_input := $(compress_output)  
  67. symbolic_output := $(LOCAL_UNSTRIPPED_PATH)/$(LOCAL_BUILT_MODULE_STEM)  
  68. $(symbolic_output) : $(symbolic_input) | $(ACP)  
  69.     @echo "target Symbolic: $(PRIVATE_MODULE) ($@)"  
  70.     $(copy-file-to-target)  
  71.   
  72. ###########################################################  
  73. ## Strip  
  74. ###########################################################  
  75. strip_input := $(symbolic_output)  
  76. strip_output := $(LOCAL_BUILT_MODULE)  
  77.   
  78. ifeq ($(strip $(LOCAL_STRIP_MODULE)),)  
  79.   LOCAL_STRIP_MODULE := $(strip $(TARGET_STRIP_MODULE))  
  80. endif  
  81.   
  82. ifeq ($(LOCAL_STRIP_MODULE),true)  
  83. # Strip the binary  
  84. $(strip_output): $(strip_input) | $(TARGET_STRIP)  
  85.     $(transform-to-stripped)  
  86. else  
  87. ......  
  88. endif # LOCAL_STRIP_MODULE  

       LOCAL_UNSTRIPPED_PATH描述的是帶符號的模塊文件的輸出目錄。如果我們沒有設置它,並且也沒有設置變量LOCAL_MODULE_PATH的值,那么它的默認值就會與當前要編譯的產品以及當前要編譯的模塊文件類型有關。例如,如果我們在執行lunch命令時,選擇的是目標產品是模擬器,並且當前要編譯的是動態鏈接庫文件,那么得到的LOCAL_UNSTRIPPED_PATH值就為TARGET_OUT_$(LOCAL_MODULE_CLASS)_UNSTRIPPED。將$(LOCAL_MODULE_CLASS)替換為SHARED_LIBRARIES,就得到LOCAL_UNSTRIPPED_PATH的值為TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED,它值就等於out/target/product/generic/symbols/system/lib。也就是說,我們在為模擬器編譯動態鏈接庫模塊時,生成的帶符號文件都保存在目錄out/target/product/generic/symbols/system/lib中。

 

       如果我們沒有設置 LOCAL_MODULE_STEM的值的話,那么它的默認值就等在我們在Android.mk文件中設置的LOCAL_MODULE的值。在我們的例子中,LOCAL_MODULE_STEM的值就等於LOCAL_MODULE的值,即libdis。

       LOCAL_INSTALLED_MODULE_STEM和LOCAL_BUILT_MODULE_STEM的值等於LOCAL_MODULE_STEM的值再加上后綴名LOCAL_MODULE_SUFFIX。在我們的例子中,LOCAL_INSTALLED_MODULE_STEM和LOCAL_BUILT_MODULE_STEM的值就等於libdis.so。

       這里調用函數local-intermediates-dir得到的是動態鏈接文件的中間輸出目錄,默認就是out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates了,因此,我們就可以得到變量linked_module的值為out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so,這是編譯過程要生成的文件之一。

       LOCAL_INTERMEDIATE_TARGETS的值被設置為linked_module的值,接下來在加載build/core/binary.mk文件時需要用到。

       接下來會根據out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so文件生成另外三個文件:

       1. 生成帶符號壓縮的模塊文件,前提是LOCAL_COMPRESS_MODULE_SYMBOLS的值等於true。輸入是compress_input,即linked_module,輸出是compress_output,即$(intermediates)/COMPRESSED-$(LOCAL_BUILT_MODULE_STEM)。注意。目前還不支持此類型的模塊文件。因此,當變量LOCAL_COMPRESS_MODULE_SYMBOLS的值等於true時,就會報錯。

       2. 拷貝一份帶符號的模塊文件到LOCAL_UNSTRIPPED_PATH描述的目錄中去,即out/target/product/generic/symbols/system/lib目錄。在我們這個情景中,得到的文件即為out/target/product/generic/symbols/system/lib/libdis.so。

       3. 生成不帶符號的模塊文件,前提是LOCAL_STRIP_MODULE的值等於true。輸入是前面拷貝到out/target/product/generic/symbols/system/lib目錄的文件,輸出由變量LOCAL_BUILT_MODULE指定。變量LOCAL_BUILT_MODULE的值是在加載文件build/core/binary.mk的過程中指定的。

       到目前為止,我們就解決前面提到的文件out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so和out/target/product/generic/symbols/system/lib/libdis.so的生成過程,還剩下out/target/product/generic/obj/lib/libdis.so和out/target/product/generic/system/lib/libdis.so文件的生成過程未搞清楚。這就需要繼續分析文件build/core/binary.mk的加載過程。

       文件build/core/binary.mk的核心內容如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. ifdef LOCAL_SDK_VERSION  
  2.   # Get the list of INSTALLED libraries as module names.  
  3.   # We cannot compute the full path of the LOCAL_SHARED_LIBRARIES for  
  4.   # they may cusomize their install path with LOCAL_MODULE_PATH  
  5.   installed_shared_library_module_names := \  
  6.       $(LOCAL_SHARED_LIBRARIES)  
  7. else  
  8.   installed_shared_library_module_names := \  
  9.       $(LOCAL_SYSTEM_SHARED_LIBRARIES) $(LOCAL_SHARED_LIBRARIES)  
  10. endif  
  11. # The real dependency will be added after all Android.mks are loaded and the install paths  
  12. # of the shared libraries are determined.  
  13. LOCAL_REQUIRED_MODULES += $(installed_shared_library_module_names)  
  14.   
  15. #######################################  
  16. include $(BUILD_SYSTEM)/base_rules.mk  
  17. #######################################  
  18.   
  19. ......  
  20.   
  21. ###########################################################  
  22. ## C++: Compile .cpp files to .o.  
  23. ###########################################################  
  24.   
  25. # we also do this on host modules, even though  
  26. # it's not really arm, because there are files that are shared.  
  27. cpp_arm_sources    := $(patsubst %$(LOCAL_CPP_EXTENSION).arm,%$(LOCAL_CPP_EXTENSION),$(filter %$(LOCAL_CPP_EXTENSION).arm,$(LOCAL_SRC_FILES)))  
  28. cpp_arm_objects    := $(addprefix $(intermediates)/,$(cpp_arm_sources:$(LOCAL_CPP_EXTENSION)=.o))  
  29.   
  30. cpp_normal_sources := $(filter %$(LOCAL_CPP_EXTENSION),$(LOCAL_SRC_FILES))  
  31. cpp_normal_objects := $(addprefix $(intermediates)/,$(cpp_normal_sources:$(LOCAL_CPP_EXTENSION)=.o))  
  32.   
  33. $(cpp_arm_objects):    PRIVATE_ARM_MODE := $(arm_objects_mode)  
  34. $(cpp_arm_objects):    PRIVATE_ARM_CFLAGS := $(arm_objects_cflags)  
  35. $(cpp_normal_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)  
  36. $(cpp_normal_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)  
  37.   
  38. cpp_objects        := $(cpp_arm_objects) $(cpp_normal_objects)  
  39.   
  40. ifneq ($(strip $(cpp_objects)),)  
  41. $(cpp_objects): $(intermediates)/%.o: \  
  42.     $(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \  
  43.     $(yacc_cpps) $(proto_generated_headers) $(my_compiler_dependencies) \  
  44.     $(LOCAL_ADDITIONAL_DEPENDENCIES)  
  45.     $(transform-$(PRIVATE_HOST)cpp-to-o)  
  46. -include $(cpp_objects:%.o=%.P)  
  47. endif  
  48.   
  49. ......  
  50.   
  51. ###########################################################  
  52. ## C: Compile .c files to .o.  
  53. ###########################################################  
  54.   
  55. c_arm_sources    := $(patsubst %.c.arm,%.c,$(filter %.c.arm,$(LOCAL_SRC_FILES)))  
  56. c_arm_objects    := $(addprefix $(intermediates)/,$(c_arm_sources:.c=.o))  
  57.   
  58. c_normal_sources := $(filter %.c,$(LOCAL_SRC_FILES))  
  59. c_normal_objects := $(addprefix $(intermediates)/,$(c_normal_sources:.c=.o))  
  60.   
  61. $(c_arm_objects):    PRIVATE_ARM_MODE := $(arm_objects_mode)  
  62. $(c_arm_objects):    PRIVATE_ARM_CFLAGS := $(arm_objects_cflags)  
  63. $(c_normal_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)  
  64. $(c_normal_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)  
  65.   
  66. c_objects        := $(c_arm_objects) $(c_normal_objects)  
  67.   
  68. ifneq ($(strip $(c_objects)),)  
  69. $(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(proto_generated_headers) \  
  70.     $(my_compiler_dependencies) $(LOCAL_ADDITIONAL_DEPENDENCIES)  
  71.     $(transform-$(PRIVATE_HOST)c-to-o)  
  72. -include $(c_objects:%.o=%.P)  
  73. endif  
  74.   
  75. ......  
  76.   
  77. # some rules depend on asm_objects being first.  If your code depends on  
  78. # being first, it's reasonable to require it to be assembly  
  79. all_objects := \  
  80.     $(asm_objects) \  
  81.     $(cpp_objects) \  
  82.     $(gen_cpp_objects) \  
  83.     $(gen_asm_objects) \  
  84.     $(c_objects) \  
  85.     $(gen_c_objects) \  
  86.     $(objc_objects) \  
  87.     $(yacc_objects) \  
  88.     $(lex_objects) \  
  89.     $(proto_generated_objects) \  
  90.     $(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES))  
  91.   
  92. ......  
  93.   
  94. ifdef LOCAL_SDK_VERSION  
  95. built_shared_libraries := \  
  96.     $(addprefix $($(my_prefix)OUT_INTERMEDIATE_LIBRARIES)/, \  
  97.       $(addsuffix $(so_suffix), \  
  98.         $(LOCAL_SHARED_LIBRARIES)))  
  99.   
  100. my_system_shared_libraries_fullpath := \  
  101.     $(my_ndk_stl_shared_lib_fullpath) \  
  102.     $(addprefix $(my_ndk_version_root)/usr/lib/, \  
  103.         $(addsuffix $(so_suffix), $(LOCAL_SYSTEM_SHARED_LIBRARIES)))  
  104.   
  105. built_shared_libraries += $(my_system_shared_libraries_fullpath)  
  106. LOCAL_SHARED_LIBRARIES += $(LOCAL_SYSTEM_SHARED_LIBRARIES)  
  107. else  
  108. LOCAL_SHARED_LIBRARIES += $(LOCAL_SYSTEM_SHARED_LIBRARIES)  
  109. built_shared_libraries := \  
  110.     $(addprefix $($(my_prefix)OUT_INTERMEDIATE_LIBRARIES)/, \  
  111.       $(addsuffix $(so_suffix), \  
  112.         $(LOCAL_SHARED_LIBRARIES)))  
  113. endif  
  114.   
  115. built_static_libraries := \  
  116.     $(foreach lib,$(LOCAL_STATIC_LIBRARIES), \  
  117.       $(call intermediates-dir-for, \  
  118.         STATIC_LIBRARIES,$(lib),$(LOCAL_IS_HOST_MODULE))/$(lib)$(a_suffix))  
  119.   
  120. ifdef LOCAL_SDK_VERSION  
  121. built_static_libraries += $(my_ndk_stl_static_lib)  
  122. endif  
  123.   
  124. built_whole_libraries := \  
  125.     $(foreach lib,$(LOCAL_WHOLE_STATIC_LIBRARIES), \  
  126.       $(call intermediates-dir-for, \  
  127.         STATIC_LIBRARIES,$(lib),$(LOCAL_IS_HOST_MODULE))/$(lib)$(a_suffix))  
  128.   
  129. ......  
  130.   
  131. ###########################################################  
  132. # Define library dependencies.  
  133. ###########################################################  
  134. # all_libraries is used for the dependencies on LOCAL_BUILT_MODULE.  
  135. all_libraries := \  
  136.     $(built_shared_libraries) \  
  137.     $(built_static_libraries) \  
  138.     $(built_whole_libraries)  
  139.   
  140. ......  

        文件build/core/binary.mk的加載邏輯如下所示:

        1. 獲得當前編譯的模塊所依賴的動態鏈接庫,也就是我們在Android.mk文件中通過LOCAL_SHARED_LIBRARIES變量引用的動態鏈接庫。注意,如果定義了變量LOCAL_SDK_VERSION,那么就表示是在SDK環境下編譯,這時候是不可以使用一些隱藏的系統動態鏈接庫。這些隱藏的系統動態鏈接庫由變量LOCAL_SYSTEM_SHARED_LIBRARIES描述。最終獲得的依賴動態鏈接庫保存在變量LOCAL_REQUIRED_MODULES中。前面我們在分析build/core/main.mk的加載過程時提到,Android編譯系統會為當前編譯的模塊所依賴的每一個模塊都生成一個依賴規則,用來保證編譯出來的當前模塊是最新的。

        2. 加載另外一個腳本文件build/core/base_rules.mk,用來計算一些基本變量的值,以及創建一些基本的依賴規則。

        3. 根據LOCAL_SRC_FILES和LOCAL_CPP_EXTENSION定義的C++文件制定對應的C++目標文件的依賴規則,並且通過函數transform-$(PRIVATE_HOST)cpp-to-o執行這些規則,實際上就是調用gcc來編譯相應的C++源文件。注意,當我們是為目標機器編譯模塊時,變量PRIVATE_HOST的值為空,因此,這時候實際上是調用transform-cpp-to-o來將.cpp源文件編譯成.o目標文件。

       4.  同樣被制定依賴規則的還包括在LOCAL_SRC_FILES中引用的C文件、匯編文件、YACC文件和LEX文件等等。最終得到的所有目標文件都保存在變量all_objects中。

       5.  獲得LOCAL_SHARED_LIBRARIES定義的各個動態依賴庫的文件路徑,並且保存在變量built_shared_libraries中。

       6.  獲得LOCAL_STATIC_LIBRARIES定義的各個靜態依賴庫的文件路徑,並且保存在變量built_static_libraries中。

       7.  獲得LOCAL_WHOLE_STATIC_LIBRARIES定義的各個要完全靜態鏈入當前編譯模塊的依賴庫的文件路徑,並且保存在變量built_whole_libraries中。

       8.  將所有獲得的靜態和動態依賴庫文件路徑保存在變量all_libraries中。

       至此,變量all_objects和all_libraries就描述了當前模塊依賴的所有目標文件和庫文件。前面在分析build/core/shared_library.mk的加載過程時提到一個依賴規則,也就是變量linked_module定義的文件依賴於變量all_objects和all_libraries的文件。也就是說,在文件build/core/binary.mk文件加載完成之后,我們就可以獲得由變量linked_module所定義的文件。在我們這個情景中,變量linked_module定義的文件就是out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so。

       在我們這個情景中,前面提到的out/target/product/generic/obj/lib/libdis.so和out/target/product/generic/system/lib/libdis.so文件的生過過程我們依然還沒有搞清楚,這就需要進一步分析build/core/base_rules.mk文件。

       文件build/core/base_rules.mk的相關內容如下所示:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. ......  
  2.   
  3. LOCAL_MODULE_PATH := $(strip $(LOCAL_MODULE_PATH))  
  4. ifeq ($(LOCAL_MODULE_PATH),)  
  5.   LOCAL_MODULE_PATH := $($(my_prefix)OUT$(partition_tag)_$(LOCAL_MODULE_CLASS))  
  6.   ifeq ($(strip $(LOCAL_MODULE_PATH)),)  
  7.     $(error $(LOCAL_PATH): unhandled LOCAL_MODULE_CLASS "$(LOCAL_MODULE_CLASS)")  
  8.   endif  
  9. endif  
  10. ......  
  11.   
  12. LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE_STEM)$(LOCAL_MODULE_SUFFIX)  
  13. ......  
  14.   
  15. intermediates := $(call local-intermediates-dir)  
  16. ......  
  17.   
  18. # OVERRIDE_BUILT_MODULE_PATH is only allowed to be used by the  
  19. # internal SHARED_LIBRARIES build files.  
  20. OVERRIDE_BUILT_MODULE_PATH := $(strip $(OVERRIDE_BUILT_MODULE_PATH))  
  21. ifdef OVERRIDE_BUILT_MODULE_PATH  
  22.   ifneq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)  
  23.     $(error $(LOCAL_PATH): Illegal use of OVERRIDE_BUILT_MODULE_PATH)  
  24.   endif  
  25.   built_module_path := $(OVERRIDE_BUILT_MODULE_PATH)  
  26. else  
  27.   built_module_path := $(intermediates)  
  28. endif  
  29. LOCAL_BUILT_MODULE := $(built_module_path)/$(LOCAL_BUILT_MODULE_STEM)  
  30. ......  
  31.   
  32. ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))  
  33.   LOCAL_INSTALLED_MODULE := $(LOCAL_MODULE_PATH)/$(LOCAL_INSTALLED_MODULE_STEM)  
  34. endif  
  35. ......  
  36.   
  37. # Provide a short-hand for building this module.  
  38. # We name both BUILT and INSTALLED in case  
  39. # LOCAL_UNINSTALLABLE_MODULE is set.  
  40. .PHONY: $(LOCAL_MODULE)  
  41. $(LOCAL_MODULE): $(LOCAL_BUILT_MODULE) $(LOCAL_INSTALLED_MODULE)  
  42. ......  
  43.   
  44. ifndef LOCAL_UNINSTALLABLE_MODULE  
  45.   # Define a copy rule to install the module.  
  46.   # acp and libraries that it uses can't use acp for  
  47.   # installation;  hence, LOCAL_ACP_UNAVAILABLE.  
  48. ifneq ($(LOCAL_ACP_UNAVAILABLE),true)  
  49. $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE) | $(ACP)  
  50.     @echo "Install: $@"  
  51.     $(copy-file-to-new-target)  
  52. else  
  53. $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)  
  54.     @echo "Install: $@"  
  55.     $(copy-file-to-target-with-cp)  
  56. endif  
  57. ......  
  58.   
  59. ALL_MODULES += $(LOCAL_MODULE)  
  60. ......  

       文件build/core/base_rules.mk的加載過程如下所示:

 

       1. 如果我們沒有在Android.mk文件中定義LOCAL_MODULE_PATH,那么LOCAL_MODULE_PATH的值就設置為$($(my_prefix)OUT$(partition_tag)_$(LOCAL_MODULE_CLASS))。它的具體值與執行lunch命令時選擇的目標產品和當前要編譯的模塊類型有關,例如,當我們編譯的是動態鏈接庫,並且目標機器是模擬器時,它的值就等於out/target/product/generic/system/lib。

        2. 前面在加載文件build/core/dynamic_binary.mk時提到,LOCAL_MODULE_STEM的值默認等於LOCAL_MODULE的值,而LOCAL_MODULE_SUFFIX的值等於.so,因此,這里得到的LOCAL_INSTALLED_MODULE_STEM的值就等於當前要編譯的模塊名稱再加上其對應的后綴名。

        3. 函數local-intermediates-dir的返回值執行lunch命令時選擇的目標產品和當前要編譯的模塊類型有關。例如,當我們編譯的是動態鏈接庫,並且目標機器是模擬器時,它的值就等於out/target/product/generic/obj/lib,也就是intermediates的值等於out/target/product/generic/obj/lib。

        4. 如果我們沒有在Android.mk文件中定義OVERRIDE_BUILT_MODULE_PATH的值,那么就表示要將生成的不帶符號的模塊文件保存在intermediates指定的目錄中。

        5. 經過上面的准備工作之后,我們就得到LOCAL_BUILT_MODULE的值等於$(built_module_path)/$(LOCAL_BUILT_MODULE_STEM)。也就是它描述的是當前模塊在編譯的過程中產生的不帶符號模塊文件路徑。在我們這個情景中,實際上就是out/target/product/generic/obj/lib/libdis.so。

        6. 如果我們沒有在Android.mk文件中將LOCAL_UNINSTALLABLE_MODULE的值設置為true,那么就表示我們需要將最終的不帶符號的模塊文件拷貝到變量LOCAL_MODULE_PATH所描述的目錄中,並且文件名為LOCAL_INSTALLED_MODULE_STEM。在我們這個情景中,實際上拷貝得到的文件就是out/target/product/generic/system/lib/libdis.so,並且通過變量LOCAL_INSTALLED_MODULE來描述。

        7. 制定偽目標LOCAEL_MODULE的依賴規則,即它依賴於變量LOCAL_BUILT_MODULE和LOCAL_INSTALLED_MODULE定義的文件。在我們這個情景中,實際上就是定義了一個偽目標libdis,它依賴於out/target/product/generic/obj/lib/libdis.so和out/target/product/generic/system/lib/libdis.so文件。

        8. 前面提到,如果我們沒有在Android.mk文件中定義LOCAL_UNINSTALLABLE_MODULE,那么就表示要將最終的不帶符號的模塊文件拷貝到變量LOCAL_MODULE_PATH所描述的目錄中,即將LOCAL_BUILT_MODULE描述的文件拷貝為LOCAL_INSTALLED_MODULE描述的文件。這時候我們還需要制定一個LOCAL_INSTALLED_MODULE依賴LOCAL_BUILT_MODULE的規則。

        9. 將表示當前模塊名稱的LOCAL_MODULE值附加到ALL_MODULES后面去。

        通過第5步我們確定了LOCAL_BUILT_MODULE的值。前面對build/core/dynamic_binary.mk文件的加載過程分析中提到,LOCAL_BUILT_MODULE描述的文件依賴於out/target/product/generic/symbols/system/lib/libdis.so文件。也就是說,out/target/product/generic/obj/lib/libdis.so文件依賴於out/target/product/generic/symbols/system/lib/libdis.so文件。同樣,通過第6步我們確定了out/target/product/generic/system/lib/libdis.so依賴於out/target/product/generic/obj/lib/libdis.so文件。

       至此,我們就搞清楚了前面提到的四個文件out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so、out/target/product/generic/symbols/system/lib/libdis.so、out/target/product/generic/obj/lib/libdis.so和out/target/product/generic/system/lib/libdis.so之間從右到左的依賴關系以及生成過程。

       然而,我們前面又提到,在執行mmm命令時,默認的make目標為all_modules,並且該目標依賴於變量ALL_MODULES所描述的偽目標,而變量ALL_MODULES所描述的偽目標又等於當前要編譯的模塊名稱,即LOCAL_MODULE。最后,LOCAL_MODULE也是一個偽目標,它依賴於LOCAL_BUILT_MODULE和LOCAL_INSTALLED_MODULE所定義的文件,也就是out/target/product/generic/obj/lib/libdis.so和out/target/product/generic/system/lib/libdis.so文件。

       這樣,我們就可以得到在我們所分析的情景中,上述各個make目標或者文件的之間的鏈式依賴關系:

       1. all_modules依賴於ALL_MODULES;

       2. ALL_MODULES依賴於LOCAL_MODULE;

       3. LOCAL_MODULE依賴於out/target/product/generic/obj/lib/libdis.so和out/target/product/generic/system/lib/libdis.so;

       4. out/target/product/generic/system/lib/libdis.so依賴於out/target/product/generic/obj/lib/libdis.so;

       5. out/target/product/generic/obj/lib/libdis.so依賴於out/target/product/generic/symbols/system/lib/libdis.so;

       6. out/target/product/generic/symbols/system/lib/libdis.so依賴於out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so;

       7. out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so依賴於all_objects和all_libraries;

       8. all_objects依賴於在Android.mk文件中通過LOCAL_SRC_FILES定義的文件dispatcher.cpp和common.cpp文件;

       9. all_libraries依賴於在Android.mk文件中通過LOCAL_SHARED_LIBRARIES定義的文件libdl.so和liblog.so文件。

       通過調用gcc等編譯工具,就可以從最后一步開始,一步步向上編譯出各個模塊文件,並且保存在合適的位置中。

       至此,我們就分析完成了使用mmm命令來編譯一個動態鏈接庫的過程。使用mmm命令來編譯其它類型的模塊,例如APK文件、EXE文件和JAVA庫,過程也是差不多的,無非都是建立各個文件之間的依賴關系,以及調用相應的編譯工具來進行編譯。弄懂了mmm命令的編譯過程之后, 另外的三個編譯命令m、mm和make也可以一目了然了。

       當在Android源碼中定義的各個模塊都編譯好之后,我們還需要將編譯得到的文件打包成相應的鏡像文件,例如system.img、boot.img和recorvery.img等,這樣我們才可以將這些鏡像燒錄到目標設備去運行。在接下來的一篇文章中,我們就將繼續分析Android鏡像文件的打包過程,敬請關注!更多信息可以關注老羅的新浪微博:http://weibo.com/shengyangluo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM