理解Android編譯命令

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

1、引言

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

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

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

2、編譯命令

準備好編譯環境後,編譯Android源碼的第一步是 source build/envsetup.sh,其中source命令就是用於運行shell腳本命令,功能等價於".",所以該命令也等價於. build/envsetup.sh。在文件envsetup.sh聲明瞭當前會話終端可用的命令,這裏須要注意的是當前會話終端,也就意味着每次新打開一個終端都必須再一次執行這些指令。起初並不理解爲何新開的終端不能直接執行make指令,到這裏總算明白了。git

接下來,解釋一下本文開頭的引用的命令:shell

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

全部的編譯命令都在envsetup.sh文件能找到相對應的function,好比上述的命令lunchmake,在文件必定能找到api

function lunch(){
    ...
}

function make(){
    ...
}

具體實現這裏就不展開說明,下面精煉地總結了一下各個指令用法和功效。微信

2.1 代碼編譯

編譯指令 解釋
m 在源碼樹的根目錄執行編譯
mm 編譯當前路徑下全部模塊,但不包含依賴
mmm [module_path] 編譯指定路徑下全部模塊,但不包含依賴
mma 編譯當前路徑下全部模塊,且包含依賴
mmma [module_path] 編譯指定路徑下全部模塊,且包含依賴
make [module_name] 無參數,則表示編譯整個Android代碼

下面列舉部分模塊的編譯指令:app

模塊 make命令 mmm命令
init make init mmm system/core/init
zygote make app_process mmm frameworks/base/cmds/app_process
system_server make services mmm frameworks/base/services
java framework make framework mmm frameworks/base
framework資源 make framework-res mmm frameworks/base/core/res
jni framework make libandroid_runtime mmm frameworks/base/core/jni
binder make libbinder mmm frameworks/native/libs/binder

上述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 代碼搜索

搜索指令 解釋
cgrep 全部C/C++文件執行搜索操做
jgrep 全部Java文件執行搜索操做
ggrep 全部Gradle文件執行搜索操做
mangrep [keyword] 全部AndroidManifest.xml文件執行搜索操做
sepgrep [keyword] 全部sepolicy文件執行搜索操做
resgrep [keyword] 全部本地res/*.xml文件執行搜索操做
sgrep [keyword] 全部資源文件執行搜索操做

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

xgrep [keyword]  //x表明的是上表的搜索指令

例如,搜索全部AndroidManifest.xml文件中的launcher關鍵字所在文件的具體位置,指令

mangrep launcher

再如,搜索全部system_app的selinux權限信息

sepgrep system_app

Tips: Android源碼很是龐大,直接採用grep來搜索代碼,不只方法笨拙、浪費時間,並且搜索出不少無心義的混淆結果。根據具體需求,來選擇合適的代碼搜索指令,能節省代碼搜索時間,提升搜索結果的精準度,方便定位目標代碼。

2.3 導航指令

導航指令 解釋
croot 切換至Android根目錄
cproj 切換至工程的根目錄
godir [filename] 跳轉到包含某個文件的目錄

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

2.4信息查詢

查詢指令 解釋
hmm 查詢全部的指令help信息
findmakefile 查詢當前目錄所在工程的Android.mk文件路徑
print_lunch_menu 查詢lunch可選的product
printconfig 查詢各項編譯變量值
gettop 查詢Android源碼的根目錄
gettargetarch 獲取TARGET_ARCH值

Tips: 當忘了前面的全部指令時,能夠執行一個hmm即可輸出這些指令的幫助信息。

其餘指令:

  • make clean:執行清理操做,等價於 rm -rf out/

  • make update-api:更新API,在framework API改動後需執行該指令,Api記錄在目錄frameworks/base/api

3、編譯系統

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:簽署當前應用的證書名稱,好比flatform。

  • LOCAL_MODULE_TAGS:當前模塊所包含的標籤,能夠包含多標籤,可能值爲debgu,eng,user,development或optional(默認值)

針對這些環境變量,編譯系統還定義了一些便捷函數,以下:

  • $(call my-dir):獲取當前文件夾路徑;

  • $(call all-java-files-under, <src>):獲取指定目錄下的全部Java文件;

  • $(call all-c-files-under, <src>):獲取指定目錄下的全部C文件;

  • $(call all-Iaidl-files-under, <src>) :獲取指定目錄下的全部AIDL文件;

  • $(call all-makefiles-under, <folder>):獲取指定目錄下的全部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)

若是以爲本文對您有所幫助,請關注個人微信公衆號:gityuan微博:Gityuan。 或者點擊這裏查看更多關於gityuan我的信息

相關文章
相關標籤/搜索