日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android编译系统分析二:mm编译单个模块

發布時間:2025/3/21 Android 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android编译系统分析二:mm编译单个模块 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

因為Android的編譯系統不同于Linux Kernel的遞歸式的編譯系統,它的編譯系統是一種稱之為independent的模式,每個模塊基本獨立(它有可能依賴其他模塊),每個模塊都可以單獨編譯,這是Android independent編譯系統模式的好處。但這并不意味著它是完美的,普通電腦編譯android系統需要8個小時甚至更多(以本人的電腦為例),而編譯linux kernel只需要半個小時,代碼量是一回事,由independent模式造成的編譯時間長應該是可以肯定的。正因為每個模塊可以單獨編譯,所以android系統的編譯就是依次編譯每個模塊,然后把所有編譯好的模塊和其他一些文件一起打包成鏡像文件。因此,只要理解了每個模塊的編譯,理解android系統的編譯就輕松多了。(以上均是個人觀點,歡迎拍磚)

在我們source build/envsetup.sh 和 lunch 后,就可以執行mm命令編譯單個模塊了:

所以,編譯的其實位置從mm說起:

[java]?view plaincopy
  • function?mm()??
  • {??
  • ????local?T=$(gettop)??
  • ????local?DRV=$(getdriver?$T)??
  • ????#?If?we're?sitting?in?the?root?of?the?build?tree,?just?do?a??
  • ????#?normal?make.??
  • ????if?[?-f?build/core/envsetup.mk?-a?-f?Makefile?];?then??
  • ????????$DRV?make?$@??
  • ????else??
  • ????????#?Find?the?closest?Android.mk?file.??
  • ????????local?M=$(findmakefile)??
  • ????????local?MODULES=??
  • ????????local?GET_INSTALL_PATH=??
  • ????????local?ARGS=??
  • ????????#?Remove?the?path?to?top?as?the?makefilepath?needs?to?be?relative??
  • ????????local?M=`echo?$M|sed?'s:'$T'/::'`??
  • ????????if?[?!?"$T"?];?then??
  • ????????????echo?"Couldn't?locate?the?top?of?the?tree.??Try?setting?TOP."??
  • ????????????return?1??
  • ????????elif?[?!?"$M"?];?then??
  • ????????????echo?"Couldn't?locate?a?makefile?from?the?current?directory."??
  • ????????????return?1??
  • ????????else??
  • ????????????for?ARG?in?$@;?do??
  • ????????????????case?$ARG?in??
  • ??????????????????GET-INSTALL-PATH)?GET_INSTALL_PATH=$ARG;;??
  • ????????????????esac??
  • ????????????done??
  • ????????????if?[?-n?"$GET_INSTALL_PATH"?];?then??
  • ??????????????MODULES=??
  • ??????????????ARGS=GET-INSTALL-PATH??
  • ????????????else??
  • ??????????????MODULES=all_modules??
  • ??????????????ARGS=$@??
  • ????????????fi??
  • ????????????ONE_SHOT_MAKEFILE=$M?$DRV?make?-C?$T?-f?build/core/main.mk?$MODULES?$ARGS??
  • ????????fi??
  • ????fi??
  • }??

  • 這個函數做了三件事情:1.找到Android.mk文件,2.設置ONE_SHOT_MAKEFILE=$M,3.執行make all_modules進行編譯

    1.findmakefile:

    [java]?view plaincopy
  • function?findmakefile()??
  • {??
  • ????TOPFILE=build/core/envsetup.mk??
  • ????local?HERE=$PWD??
  • ????T=??
  • ????while?[?!\(?f$TOPFILE!\(?f$TOPFILE?\)?-a?\(?$PWD?!=?"/"?\)?];?do??
  • ????????T=`PWD=?/bin/pwd`??
  • ????????if?[?-f?"$T/Android.mk"?];?then??
  • ????????????echo?$T/Android.mk??
  • ????????????\cd?$HERE??
  • ????????????return??
  • ????????fi??
  • ????????\cd?..??
  • ????done??
  • ????\cd?$HERE??
  • }??
  • 這個函數首先在當前目錄下查找Android.mk,如果沒有就向上查找。

    2.ONE_SHOT_MAKEFILE=$M

    $M = $(findmakefile),所以它就是用來編譯的那個模塊的Android.mk,一般情況下,如果你在當前目錄下執行mm,而且當前目錄下如果有個Android.mk的話,那她就是這個Android.mk的路勁+Android.mk了。

    3.make -C $T -f build/core/main.mk $MODULES $ARGS

    -C $T表明還是在源碼頂級目錄下執行make的,傳入的參數一個是$MODULES=all_modules,$ARGS為空

    這個時候,代碼機會執行頂級的Makefile:

    [java]?view plaincopy
  • ###?DO?NOT?EDIT?THIS?FILE?###??
  • include?build/core/main.mk??
  • ###?DO?NOT?EDIT?THIS?FILE?###??
  • 加載main.mk


    main.mk往下加載,不久我們就看到了我們在mm函數中設置的ONE_SHOT_MAKEFILE變量了:

    [java]?view plaincopy
  • ifneq?($(ONE_SHOT_MAKEFILE),)??
  • #?We've?probably?been?invoked?by?the?"mm"?shell?function??
  • #?with?a?subdirectory's?makefile.??
  • include?$(ONE_SHOT_MAKEFILE)??
  • #?Change?CUSTOM_MODULES?to?include?only?modules?that?were??
  • #?defined?by?this?makefile;?this?will?install?all?of?those??
  • #?modules?as?a?side-effect.??Do?this?after?including?ONE_SHOT_MAKEFILE??
  • #?so?that?the?modules?will?be?installed?in?the?same?place?they??
  • #?would?have?been?with?a?normal?make.??
  • CUSTOM_MODULES?:=?$(sort?$(call?get-tagged-modules,$(ALL_MODULE_TAGS)))??
  • FULL_BUILD?:=??
  • #?Stub?out?the?notice?targets,?which?probably?aren't?defined??
  • #?when?using?ONE_SHOT_MAKEFILE.??
  • NOTICE-HOST-%:?;??
  • NOTICE-TARGET-%:?;??
  • ??
  • #?A?helper?goal?printing?out?install?paths??
  • .PHONY:?GET-INSTALL-PATH??
  • GET-INSTALL-PATH:??
  • ????@$(foreach?m,?$(ALL_MODULES),?$(if?$(ALL_MODULES.$(m).INSTALLED),?\??
  • ????????echo?'INSTALL-PATH:?$(m)?$(ALL_MODULES.$(m).INSTALLED)';))??
  • ??
  • else?#?ONE_SHOT_MAKEFILE??
  • 這里判斷ONE_SHOT_MAKEFILE是否為空,當然不為空了。緊接著開始加載這個Android.mk,也就是我們要編譯的那個Android.mk。簡單起見,這里以frameworks/base/cmds/screencap模塊的編譯為例,它的內容如下:

    [java]?view plaincopy
  • LOCAL_PATH:=?$(call?my-dir)??
  • include?$(CLEAR_VARS)??
  • ??
  • LOCAL_SRC_FILES:=?\??
  • ????screencap.cpp??
  • ??
  • LOCAL_SHARED_LIBRARIES?:=?\??
  • ????libcutils?\??
  • ????libutils?\??
  • ????libbinder?\??
  • ????libskia?\??
  • ????libui?\??
  • ????libgui??
  • ??
  • LOCAL_MODULE:=?screencap??
  • ??
  • LOCAL_MODULE_TAGS?:=?optional??
  • ??
  • LOCAL_CFLAGS?+=?-Wall?-Werror?-Wunused?-Wunreachable-code??
  • ??
  • include?$(BUILD_EXECUTABLE)??
  • 它的變量非常少,這很有利于我們搞清它編譯的過程。include 這個Android.mk后,又include $(CLEAR_VARS)
    [java]?view plaincopy
  • core/config.mk:69:CLEAR_VARS:=?$(BUILD_SYSTEM)/clear_vars.mk??
  • CLEAR_VARS定義在config.mk文件中,它指向一個clear_vars.mk文件:

    [java]?view plaincopy
  • LOCAL_MODULE:=??
  • LOCAL_MODULE_PATH:=??
  • LOCAL_MODULE_RELATIVE_PATH?:=??
  • LOCAL_MODULE_STEM:=??
  • LOCAL_DONT_CHECK_MODULE:=??
  • LOCAL_CHECKED_MODULE:=??
  • LOCAL_BUILT_MODULE:=??
  • LOCAL_BUILT_MODULE_STEM:=??
  • [java]?view plaincopy
  • 。。。??
  • 這個文件就是把一大堆的文件置為空,除了LOCAL_PATH變量之外。

    接著,它有include BUILD_EXTABLE指向的腳本。

    [java]?view plaincopy
  • core/config.mk:74:BUILD_EXECUTABLE:=?$(BUILD_SYSTEM)/executable.mk??
  • BUILD_EXTABLE變量也定義在config.mk中,它指向的excutable.mk腳本內容如下:




    關于閱讀Makefile,個人觀點就是緊追依賴鏈。我們執行的make的時候不是傳了一個目標叫all_mudules了嗎?所以make就會從它開始推導依賴關系,然后從依賴鏈的最葉子的位置生成目標,一次向上。所以那就看看all_modules:

    [java]?view plaincopy
  • #?phony?target?that?include?any?targets?in?$(ALL_MODULES)??
  • .PHONY:?all_modules??
  • ifndef?BUILD_MODULES_IN_PATHS??
  • all_modules:?$(ALL_MODULES)??
  • else??
  • #?BUILD_MODULES_IN_PATHS?is?a?list?of?paths?relative?to?the?top?of?the?tree??
  • module_path_patterns?:=?$(foreach?p,?$(BUILD_MODULES_IN_PATHS),\??
  • ????$(if?$(filter?%/,$(p)),$(p)%,$(p)/%))??
  • my_all_modules?:=?$(sort?$(foreach?m,?$(ALL_MODULES),$(if?$(filter\??
  • ????$(module_path_patterns),?$(addsuffix?/,$(ALL_MODULES.$(m).PATH))),$(m))))??
  • all_modules:?$(my_all_modules)??
  • endif??
  • all_modules的依賴取決于有沒有定義BUILD_MODULES_IN_PATHS,然而我們并有定義它,所以它就all_modules的依賴就是$(ALL_MODULES)。

    至此,就需要我們一步步推導依賴關系了,為方便理解,現直接把依賴關系以圖的形式列出:


    由于一張顯示不完,$(linked_module)的依賴如下:




    圖中的變量未經推導,為了方便對比,推導出變量的值后的圖如下:


    $(linked_module):


    從圖中可以看到最終生成的文件有:

    out/target/product/xxx/obj/excutable/screepcap__intermediates/screencap

    out/target/product/xxx/symbols/system/bin/screencap

    out/target/product/xxx/obj/excutable/screepcap__intermediates/PACKED/screencap

    out/target/product/xxx/obj/excutable/screepcap__intermediates/LINKED/screencap

    out/target/product/xxx/obj/excutable/screepcap__intermediates/screencap.o

    out/target/product/xxx/obj/excutable/screepcap__intermediates/export_includes out/target/product/xxx/obj/excutable/screepcap__intermediates/import_includes

    至于變量的推導過程,大家順著文件加載的順序慢慢推導就是了,這個過程可能比較花時間,但也是沒辦法的事。

    以下是一些重要文件的加載順序(只有部分比較重要的):


    畫圈的是我認為非常重要的文件。

    在所有依賴生成以后,Android是怎么編譯某個模塊的呢?

    以下是我認為的核心代碼,代碼在dynamic_binary.mk中:

    [java]?view plaincopy
  • $(linked_module):?$(my_target_crtbegin_dynamic_o)?$(all_objects)?$(all_libraries)?$(my_target_crtend_o)??
  • ????$(transform-o-to-executable)??
  • 還記得我們推導出來的linked_module的值嗎?它等于:

    out/target/product/xxx/obj/excutable/screepcap__intermediates/LINKED/screencap

    生成這個文件后,從依賴關系上也可以看出,其他文件在此基礎上生成,而這個文件使用transform-o-to-executable函數生成,該函數定義如下:

    [java]?view plaincopy
  • define?transform-o-to-executable??
  • @mkdir?-p?$(dir?$@)??
  • @echo?"target?Executable:?$(PRIVATE_MODULE)?($@)"??
  • $(transform-o-to-executable-inner)??
  • endef??
  • 調用transform-o-to-executable-inner函數進一步處理:

    [java]?view plaincopy
  • define?transform-o-to-executable-inner??
  • $(hide)?$(PRIVATE_CXX)?-pie?\??
  • ????-nostdlib?-Bdynamic?\??
  • ????-Wl,-dynamic-linker,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_LINKER)?\??
  • ????-Wl,--gc-sections?\??
  • ????-Wl,-z,nocopyreloc?\??
  • ????$(PRIVATE_TARGET_GLOBAL_LD_DIRS)?\??
  • ????-Wl,-rpath-link=$(PRIVATE_TARGET_OUT_INTERMEDIATE_LIBRARIES)?\??
  • ????$(if?$(filter?true,$(PRIVATE_NO_CRT)),,$(PRIVATE_TARGET_CRTBEGIN_DYNAMIC_O))?\??
  • ????$(PRIVATE_ALL_OBJECTS)?\??
  • ????-Wl,--whole-archive?\??
  • ????$(call?normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES))?\??
  • ????-Wl,--no-whole-archive?\??
  • ????$(if?$(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group)?\??
  • ????$(call?normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES))?\??
  • ????$(if?$(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group)?\??
  • ????$(if?$(filter?true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_LIBGCOV))?\??
  • ????$(if?$(filter?true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_LIBPROFILE_RT))?\??
  • ????$(PRIVATE_TARGET_LIBATOMIC)?\??
  • ????$(PRIVATE_TARGET_LIBGCC)?\??
  • ????$(call?normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES))?\??
  • ????-o?$@?\??
  • ????$(PRIVATE_TARGET_GLOBAL_LDFLAGS)?\??
  • ????$(PRIVATE_LDFLAGS)?\??
  • ????$(if?$(filter?true,$(PRIVATE_NO_CRT)),,$(PRIVATE_TARGET_CRTEND_O))?\??
  • ????$(PRIVATE_LDLIBS)??
  • endef??

  • 這個函數使用clang編譯器,最終生成了$(linked_module)目標。

    而從$(linked_module)生成out/target/product/xxx/obj/excutable/screepcap__intermediates/PACKED/screencap則使用了如下方法:

    [java]?view plaincopy
  • $(relocation_packer_output):?$(relocation_packer_input)?|?$(ACP)??
  • ????@echo?"target?Unpacked:?$(PRIVATE_MODULE)?($@)"??
  • ????$(copy-file-to-target)??
  • endif??
  • copy-file-to-target定義如下:

    [java]?view plaincopy
  • define?copy-file-to-target??
  • @mkdir?-p?$(dir?$@)??
  • $(hide)?$(ACP)?-fp?$<?$@??
  • endef??

  • 可以看就是一個簡單的拷貝,所以這兩個文件并沒有什么不同。

    生成out/target/product/xxx/symbols/system/bin/screencap也是在$(linked_module)的基礎上做拷貝:

    [java]?view plaincopy
  • $(symbolic_output)?:?$(symbolic_input)?|?$(ACP)??
  • ????@echo?"target?Symbolic:?$(PRIVATE_MODULE)?($@)"??
  • ????$(copy-file-to-target)??
  • 瀏覽其他幾個screencap文件的生成方法發現,其他幾個screencap文件都是在$(linked_module)基礎上拷貝而來,而$(linked_module)文件則使用transform-o-to-executable編譯生成。因此,到這里一個完整的可執行文件的編譯就告一段落了。編譯apk、共享庫等其他模塊的思路都與之類似,正所謂觸類旁通,只要完整掌握了一種類型模塊的編譯,其他類型的模塊編譯都變得容易理解了。


    總結

    以上是生活随笔為你收集整理的Android编译系统分析二:mm编译单个模块的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。