閱讀360 返回首頁    go 阿裏雲 go 技術社區[雲棲]


理解Android編譯命令

工欲善其事,必先利其器,對於想要深入學習Android源碼,必須先掌握Android編譯命令.

一、引言

  
關於Android Build係統,這個話題很早就打算整理下,遲遲沒有下筆,決定跟大家分享下。先看下麵幾條指令,相信編譯過Android源碼的人都再熟悉不過的。

source /opt/android1204_17.conf 
source setenv.sh
lunch
make -j12

  
記得最初剛接觸Android時,同事告訴我用上麵的指令就可以編譯Android源碼,指令雖短但過幾天就記不全或者忘記順序,每次編譯時還需要看看自己的雲筆記,冰冷的指令總是難以讓我記憶。後來我決定認真研究下這個指令的含義。知其然還需知其所以然,這樣能更深層次的理解並記憶,才能與自身的知識體係建立強連接,或許還有意外收獲,果然如此,接下來跟大家分享一下在研究上述幾條指令含義的過程中,深入了解到的Android Build(編譯)係統。

二、編譯命令

  
準備好編譯環境後,編譯Android源碼的第一步是 source build/envsetup.sh,其中source命令就是用於運行shell腳本命令,功能等價於”.”,因此該命令也等價於. build/envsetup.sh。在文件envsetup.sh聲明了當前會話終端可用的命令,這裏需要注意的是當前會話終端,也就意味著每次新打開一個終端都必須再一次執行這些指令。起初並不理解為什麼新開的終端不能直接執行make指令,到這裏總算明白了。
  
接下來,解釋一下本文開頭的引用的命令:  

source /opt/android1204_17.conf  //初始化jdk環境變量(這個不是必需的,因廠商而異)
source setenv.sh  //初始化編譯環境,包括後麵的lunch和make指令
lunch  //指定此次編譯的目標設備以及編譯類型
make  -j12 //開始編譯,默認為編譯整個係統,其中-j12代表的是編譯的job數量為12。

  
所有的編譯命令都在envsetup.sh文件能找到相對應的function,比如上述的命令lunch,make,在文件一定能找到

function lunch(){
...
}

function make(){**
...
}
  
source envsetup.sh,需要cd到setenv.sh文件所在路徑執行,路徑可能在build/envsetup.sh,或者integrate/envsetup.sh,再或者不排除有些廠商會封裝自己的.sh腳本,但核心思路是一致的。
  
具體實現這裏就不展開說明,下麵精煉地總結了一下各個指令用法和功效。

2.1 代碼編譯

image
  
下麵列舉部分模塊的編譯指令:

image
  
上述mmm命令同樣適用於mm/mma/mmma,編譯係統采用的是增量編譯,隻會編譯發生變化的目標文件。當需要重新編譯所有的相關模塊,則需要編譯命令後增加參數-B,比如make -B [module_name],或者 mm -B [module_path]。
  
Tips:

  • 對於m、mm、mmm、mma、mmma這些命令的實現都是通過make方式來完成的。
  • mmm/mm編譯的效率很高,而make/mma/mmma編譯較緩慢;
  • make/mma/mmma編譯時會把所有的依賴模塊一同編譯,但mmm/mm不會;
  • 建議:首次編譯時采用make/mma/mmma編譯;當依賴模塊已經編譯過的情況,則使用mmm/mm編譯。

2.2 代碼搜索

image
  
上述指令用法最終實現方式都是基於grep指令,各個指令用法格式:

xgrep [keyword]  //x代表的是上表的搜索指令
  
例如,搜索所有AndroidManifest.xml文件中的launcher關鍵字所在文件的具體位置,指令  

mangrep launcher
  
再如,搜索所有Java代碼中包含zygote所在文件  

jgrep zygote

又如,搜索所有system_app的selinux權限信息

sepgrep system_app
  
Tips: Android源碼非常龐大,直接采用grep來搜索代碼,不僅方法笨拙、浪費時間,而且搜索出很多無意義的混淆結果。根據具體需求,來選擇合適的代碼搜索指令,能節省代碼搜索時間,提高搜索結果的精準度,方便定位目標代碼。

2.3 導航指令

  Tips: 當每次修改完某個文件後需要編譯時,執行cproj後會跳轉到當前模塊的根目錄,也就是Android.mk文件所在目錄,然後再執行mm指令,即可編譯目標模塊;當進入源碼層級很深後,需要返回到根目錄,使用croot一條指令完成;另外cd - 指令可用於快速切換至上次目錄。

2.4 信息查詢

2.5 其他指令

  
上述隻是列舉比較常用的指令,還有其他指令,而且不同的build編譯係統,支持的指令可能會存在一些差異,當忘記這些編譯指令,可以通過執行hmm,查詢指令的幫助信息。
  
最後再列舉兩個比較常用的指令:

  • make clean:執行清理操作,等價於 rm -rf out/
  • make update-api:更新API,在framework API改動後需執行該指令,Api記錄在目錄frameworks/base/api;

三、編譯係統

  
Android 編譯係統是Android源碼的一部分,用於編譯Android係統,Android SDK以及相關文檔。該編譯係統是由Make文件、Shell以及Python腳本共同組成,其中最為重要的便是Make文件。關於編譯係統可參考 理解 Android Build 係統。

3.1 Makefile分類

 
整個Build係統的Make文件分為三大類:

  • 係統核心的Make文件:定義了Build係統的框架,文件全部位於路徑/build/core,其他Make文件都是基於該框架編寫的;
  • 針對產品的Make文件:定義了具體某個型號手機的Make文件,文件路徑位於/device,該目錄下往往又以公司名和產品名劃分兩個子級目錄,比如/device/qcom/msm8916;
  • 針對模塊的Make文件:整個係統分為各個獨立的模塊,每個模塊都一個專門的Make文件,名稱統一為”Android.mk”,該文件定義了當前模塊的編譯方式。Build係統會掃描整個源碼樹中名為”Android.mk”的問題,並執行相應模塊的編譯工作。

3.2 編譯產物

 
經過make編譯後的產物,都位於/out目錄,該目錄下主要關注下麵幾個目錄:

  • /out/host:Android開發工具的產物,包含SDK各種工具,比如adb,dex2oat,aapt等。
  • /out/target/common:通用的一些編譯產物,包含Java應用代碼和Java庫;
  • /out/target/product/[product_name]:針對特定設備的編譯產物以及平台相關C/C++代碼和二進製文件;   
    在/out/target/product/[product_name]目錄下,有幾個重量級的鏡像文件:
  • system.img:掛載為根分區,主要包含Android OS的係統文件;
  • ramdisk.img:主要包含init.rc文件和配置文件等;
  • userdata.img:被掛載在/data,主要包含用戶以及應用程序相關的數據;   
    當然還有boot.img,reocovery.img等鏡像文件,這裏就不介紹了。

3.3 Android.mk解析

  
在源碼樹中每一個模塊的所有文件通常都相應有一個自己的文件夾,在該模塊的根目錄下有一個名稱為“Android.mk” 的文件。編譯係統正是以模塊為單位進行編譯,每個模塊都有唯一的模塊名,一個模塊可以有依賴多個其他模塊,模塊間的依賴關係就是通過模塊名來引用的。也就是說當模塊需要依賴一個jar包或者apk時,必須先將jar包或apk定義為一個模塊,然後再依賴相應的模塊。
  
對於Android.mk文件,通常都是以下麵兩行

LOCAL_PATH := $(call my-dir)  //設置當編譯路徑為當前文件夾所在路徑
include $(CLEAR_VARS)  //清空編譯環境的變量(由其他模塊設置過的變量)

 
為方便模塊編譯,編譯係統設置了很多的編譯環境變量,如下:

  • LOCAL_SRC_FILES:當前模塊包含的所有源碼文件;
  • LOCAL_MODULE:當前模塊的名稱(具有唯一性);
  • LOCAL_PACKAGE_NAME:當前APK應用的名稱(具有唯一性);
  • LOCAL_C_INCLUDES:C/C++所需的頭文件路徑;
  • LOCAL_STATIC_LIBRARIES:當前模塊在靜態鏈接時需要的庫名;
  • LOCAL_SHARED_LIBRARIES:當前模塊在運行時依賴的動態庫名;
  • LOCAL_STATIC_JAVA_LIBRARIES:當前模塊依賴的Java靜態庫;
  • LOCAL_JAVA_LIBRARIES:當前模塊依賴的Java共享庫;
  • LOCAL_CERTIFICATE:簽署當前應用的證書名稱,比如platform。
  • LOCAL_MODULE_TAGS:當前模塊所包含的標簽,可以包含多標簽,可能值為debgu,eng,user,development或optional(默認值)    
    針對這些環境變量,編譯係統還定義了一些便捷函數,如下:
  • $(call my-dir):獲取當前文件夾路徑;
  • $(call all-java-files-under, ):獲取指定目錄下的所有Java文件;
  • $(call all-c-files-under, ):獲取指定目錄下的所有C文件;
  • $(call all-Iaidl-files-under, ) :獲取指定目錄下的所有AIDL文件;
  • $(call all-makefiles-under, ):獲取指定目錄下的所有Make文件;  示例:  ** LOCAL_PATH := $(call my-dir)    include $(CLEAR_VARS) **

   # 獲取所有子目錄中的Java文件
   LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
# 當前模塊依賴的動態Java庫名稱
   LOCAL_JAVA_LIBRARIES := com.gityuan.lib 

   # 當前模塊的名稱
   LOCAL_MODULE := demo 

   # 將當前模塊編譯成一個靜態的Java庫
   include $(BUILD_STATIC_JAVA_LIBRARY)

版權聲明:此文章轉載自碼農網

最後更新:2017-08-13 22:47:57

  上一篇:go  獨家:阿裏巴巴DevOps落地實踐玩法及思路解析
  下一篇:go  SEO優化實戰:如何撰寫一篇高轉化率的軟文