天天看點

了解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腳本,但核心思路是一緻的。

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

了解Android編譯指令

下面列舉部分子產品的編譯指令:

了解Android編譯指令

上述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編譯。

了解Android編譯指令

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

xgrep [keyword]  //x代表的是上表的搜尋指令

例如,搜尋所有AndroidManifest.xml檔案中的launcher關鍵字所在檔案的具體位置,指令  

mangrep launcher

再如,搜尋所有Java代碼中包含zygote所在檔案  

jgrep zygote

又如,搜尋所有system_app的selinux權限資訊

sepgrep system_app

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

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

上述隻是列舉比較常用的指令,還有其他指令,而且不同的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 系統。

 

整個Build系統的Make檔案分為三大類:

系統核心的Make檔案:定義了Build系統的架構,檔案全部位于路徑/build/core,其他Make檔案都是基于該架構編寫的;

針對産品的Make檔案:定義了具體某個型号手機的Make檔案,檔案路徑位于/device,該目錄下往往又以公司名和産品名劃分兩個子級目錄,比如/device/qcom/msm8916;

針對子產品的Make檔案:整個系統分為各個獨立的子產品,每個子產品都一個專門的Make檔案,名稱統一為”Android.mk”,該檔案定義了目前子產品的編譯方式。Build系統會掃描整個源碼樹中名為”Android.mk”的問題,并執行相應子產品的編譯工作。

經過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等鏡像檔案,這裡就不介紹了。

在源碼樹中每一個子產品的所有檔案通常都相應有一個自己的檔案夾,在該子產品的根目錄下有一個名稱為“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)

   LOCAL_JAVA_LIBRARIES := com.gityuan.lib 

   # 目前子產品的名稱

   LOCAL_MODULE := demo 

   # 将目前子產品編譯成一個靜态的Java庫

   include $(BUILD_STATIC_JAVA_LIBRARY)

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

繼續閱讀