理解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 程式碼編譯
編譯指令 | 解釋 |
---|---|
m | 在原始碼樹的根目錄執行編譯 |
mm | 編譯當前路徑下所有模組,但不包含依賴 |
mmm [module_path] | 編譯指定路徑下所有模組,但不包含依賴 |
mma | 編譯當前路徑下所有模組,且包含依賴 |
mmma [module_path] | 編譯指定路徑下所有模組,且包含依賴 |
make [module_name] | 無引數,則表示編譯整個Android程式碼 |
下面列舉部分模組的編譯指令:
模組 | 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
再如,搜尋所有Java程式碼中包含zygote所在檔案
jgrep zygote
又如,搜尋所有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值 |
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)
相關文章
- Android 原始碼中的編譯命令Android原始碼編譯
- android反編譯相關命令總結Android編譯
- 編譯Android下可執行命令的FFmpeg編譯Android
- 用命令編譯編譯
- 使用CMake命令編譯Android平臺下的包編譯Android
- Unity 編譯 Android 的原理解析和 apk 打包分析Unity編譯AndroidAPK
- clang++編譯命令編譯
- 對預編譯的理解編譯
- [android]android自動化測試六之命令列編譯APKAndroid命令列編譯APK
- 在CentOS上全命令列搭建android編譯環境CentOS命令列Android編譯
- Assimp Android 編譯Android編譯
- 編譯 Android 4.3編譯Android
- Android 編譯 pngAndroid編譯
- android 反編譯Android編譯
- android編譯方法Android編譯
- Android反編譯:反編譯工具和方法Android編譯
- React Native 編譯命令React Native編譯
- android自動化測試六之命令列編譯APKAndroid命令列編譯APK
- libusb android ndk編譯--編譯mipsAndroid編譯
- Android 反編譯指南Android編譯
- Android 增量編譯小解Android編譯
- Android 編譯優化Android編譯優化
- 編譯Android原始碼編譯Android原始碼
- 編譯Android VNC Server編譯AndroidVNCServer
- Android 編譯 zlibAndroid編譯
- android NDK 交叉編譯Android編譯
- 加快Android編譯速度Android編譯
- Windows下搭建Android NDK開發環境及命令列編譯WindowsAndroid開發環境命令列編譯
- [java]javap命令列反編譯Java命令列編譯
- Android原始碼編譯--整理Android原始碼編譯
- android使用ant編譯(rem)Android編譯REM
- android 5.1 kernel 編譯模組Android編譯
- Android編譯環境搭建Android編譯
- Gluon 編譯 JavaFx -> android apk編譯JavaAndroidAPK
- Android Apk反編譯系列教程(一)如何反編譯APKAndroidAPK編譯
- [譯] 理解編譯器 —— 從人類的角度(版本 2)編譯
- 【BBED】編譯及基本命令(1)編譯
- 配置C#命令列編譯器C#命令列編譯