Android的源碼非常的龐大,編譯Android系統往往會占用我們很長的時間,我們需要瞭解下Android的編譯規則,以期能提高我們的開發效率。。。 ...
一、需求
Android的源碼非常的龐大,編譯Android系統往往會占用我們很長的時間,我們需要瞭解下Android的編譯規則,以期能提高我們的開發效率,達到程式員按時下班的偉大理想。
二、環境
- 平臺:QCM2290
- 版本:Android 11
- 編譯伺服器: 64G + 32核
三、相關概念
3.1 Treble架構
由於Android各個層級之間的耦合性大,Android系統更新成本高,導致Android系統版本雖然已經釋放了很久,但是市面上Android的系統依然存在滯後、碎片化的情況。Android 8.0 重新設計了 Android 操作系統框架(名為“Treble”的項目),以便讓製造商能夠以更低的成本更輕鬆、更快速地將設備更新到新版 Android 系統。
Android 7.x 及更早版本中沒有正式的供應商介面。當設備製造上升級Android系統時,需要移植大量的代碼。因為Framework與供應商代碼打包在一個鏡像中,所以必須整體升級。
3.2 Soong
Soong 構建系統是在 Android 7.0 (Nougat) 中引入的,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 構建系統組件來加速 Android 的構建。
Soong是由Go語言寫的一個項目,從Android 7.0開始,在prebuilts/go/目錄下新增了Go語言所需的運行環境,Soong在編譯時使用,解析Android.bp,將之轉化為Ninja文件,完成Android的選擇編譯,解析配置工作等。故Soong相當於Makefile編譯系統的核心,即build/make/core下麵的內容。
3.3 Blueprint
Blueprint由Go語言編寫,是生成、解析Android.bp的工具,是Soong的一部分。Soong則是專為Android編譯而設計的工具,Blueprint只是解析文件的形式,而Soong則解釋內容的含義。
3.4 KATI
kati是Google專門為了Android而開發的一個小項目,基於Golang和C++。目的是為了把Android中的Makefile,轉換成Ninja文件。
在最新的Android R(11)中,Google已經移除了/build/kati目錄,只保留了一個預先編譯出來的可執行文件:prebuilts/build-tools/linux-x86/bin/ckati,這意味著Google在逐漸從編譯系統中移除kati,預計1-2個Android大版本,.mk文件全部都切換成.bp文件後,kati將會正式退出Android歷史舞臺。
kati是go語言寫的,而ckati是c++寫的。kati官方文檔對它的描述是:kati is an experimental GNU make clone。也就是說,kati是對等make命令的。只不過kati並不執行具體的編譯工作,而是生成ninja文件。kati剛開始是使用Golang編寫的,但是後來驗證下來發現編譯速度不行,於是改成C++編寫,所以現在存在兩個版本:kati、ckati。
3.5 Ninja
Ninja 是Google的一名程式員推出的註重速度的構建工具。一般在Unix/Linux上的程式通過make/makefile來構建編譯,而Ninja通過將編譯任務並行組織,大大提高了構建速度。
Ninja是一個致力於速度的小型編譯系統(類似於Make),如果把其他編譯系統比做高級語言的話,Ninja就是彙編語言。通常使用Kati或soong把makefile轉換成Ninja files,然後用Ninja編譯。
ninja核心是由C/C++編寫的,同時有一部分輔助功能由python和shell實現。由於其開源性,所以可以利用ninja的開源代碼進行各種個性化的編譯定製。
3.5 Makefile
Makefile是一個文本文件,是GNU make程式在執行的時候預設讀取的配置文件。其關係到了整個工程的編譯規則。一個工程中的源文件按類型、功能、模塊分別放在若幹個目錄中,makefile定義了一系列規則來指定,哪些文件需要先編譯,哪些文件需要後編譯,哪些文件需要重新編譯,甚至於進行更複雜的功能操作。
其好處在於:寫好makefile之後,只需要一個“make”命令,整個工程就能完全自動編譯,極大地提高了軟體開發的效率。
四、高通編譯
我司高通項目的編譯,有一個痛點,就是編譯的時間太久,即便是remake一個工程都要占用小半天的時間,不僅自身調試效率低,還頻繁占用伺服器編譯資源。故我們有必要瞭解下,高通的編譯腳本的邏輯。
4.1 編譯指令
以下為我司編譯常用指令:
平臺 | 編譯指令 | 備註 |
---|---|---|
QCM2290 | ./build.sh dist -j32 | 全編譯,並打包 |
QCM2290 | source build/envsetup.sh lunch qssi-userdebug ./build.sh dist -j32 --qssi_only |
單獨編譯,qssi模塊 |
QCM2290 | source build/envsetup.sh lunch bengal-userdebug ./build.sh dist -j32 --target_only |
單獨編譯,target模塊 |
QCM2290 | source build/envsetup.sh lunch bengal-userdebug ./build.sh dist -j32 --merge_only |
打包操作,打包ota zip and super.img |
QCM2290 | source build/envsetup.sh lunch bengal-userdebug ./build.sh -j32 --merge_only |
打包操作,打包super.img |
4.2 編譯腳本
(1)qssi模塊,qcom single system image,就類似system分區,編譯生成路徑:out\target\product\qssi;
(2)target模塊,就類似vendor分區,編譯生成路徑:out\target\product\bengal;
(3)AndroidQ以前只用lunch自己項目的target就可以進行編譯了,現在隨著Treble架構的強制推行,system和vendor要強制解耦了。
@android/build.sh
function build_qssi_only () {//編譯qssi
command "source build/envsetup.sh"
command "$QTI_BUILDTOOLS_DIR/build/kheaders-dep-scanner.sh"
command "lunch ${TARGET_QSSI}-${TARGET_BUILD_VARIANT}"
command "make $QSSI_ARGS"
}
function build_target_only () {//編譯target
command "source build/envsetup.sh"
command "$QTI_BUILDTOOLS_DIR/build/kheaders-dep-scanner.sh"
command "lunch ${TARGET}-${TARGET_BUILD_VARIANT}"
QSSI_ARGS="$QSSI_ARGS SKIP_ABI_CHECKS=$SKIP_ABI_CHECKS"
command "make $QSSI_ARGS"
command "run_qiifa"
}
function merge_only () {//打包
# DIST/OTA specific operations:
if [ "$DIST_ENABLED" = true ]; then
generate_ota_zip
fi
# Handle dynamic partition case and generate images
if [ "$BOARD_DYNAMIC_PARTITION_ENABLE" = true ]; then
generate_dynamic_partition_images
fi
}
function full_build () {//全編譯
build_qssi_only
build_target_only
# Copy Qssi system|product.img to target folder so that all images can be picked up from one folder
command "cp $QSSI_OUT/system.img $OUT/"
if [ -f $QSSI_OUT/product.img ]; then
command "cp $QSSI_OUT/product.img $OUT/"
fi
if [ -f $QSSI_OUT/system_ext.img ]; then
command "cp $QSSI_OUT/system_ext.img $OUT/"
fi
merge_only
}
ps:編譯時,根據需要,編譯對應的target模塊或者qssi模塊,儘量避免全編。
五、Android-Make指令
從高通的編譯腳本來看,其最終是調用make指令,來實現Android的編譯。那麼問題來了,這個make指令實現在哪裡呢?具體做了什麼事情呢?
5.1 Android編譯指令
Android項目的編譯,不同的ODM廠商會根據自身的編譯規則,定製不同的編譯腳本、打包腳本,但是歸根到底,都是基於如下命令進行客制化:
source build/envsetup.sh //step 1.初始化編譯環境
lunch xxx //step 2.選擇編譯目標
make -j8 //step 3.執行編譯
make snod //step 4.打包生成鏡像
5.2 初始化編譯環境
envsetup.sh腳本:主要是定義了make、mm、lunch、cgrep等相關函數,為Android系統的編譯提供支持。
@LINUX\android\build\envsetup.sh
...
function mmm()
(
call_hook ${FUNCNAME[0]} $@
if [ $? -ne 0 ]; then
return 1
fi
_trigger_build "modules-in-dirs-no-deps" "$@"
)
...
function make()
{
_wrap_build $(get_make_command "$@") "$@"
}
...
常用指令:
5.3 選擇編譯目標
lunch命令:主要作用是根據用戶輸入或者選擇的產品名來設置與具體產品相關的環境變數。
lunch 24後,生成的編譯環境變數信息:
5.4 執行編譯
make命令:主要用來編譯Android系統,生成對應的編譯文件。其中,Android10及之後,通過soong構建系統執行編譯。
5.4.1 編譯流程圖
5.4.2 shell腳本部分
step 1. make指令入口
@LINUX\android\build\envsetup.sh
function make()
{
_wrap_build $(get_make_command "$@") "$@"
}
step 2. 獲取構建方式(以前通過make構建,後續更換成soong方式),通過判斷soong_ui.bash文件是否存在,來決定系統構建方式。
@LINUX\android\build\envsetup.sh
function get_make_command()
{
if [ -f build/soong/soong_ui.bash ]; then
# Always use the real make if -C is passed in
for arg in "$@"; do
if [[ $arg == -C* ]]; then
echo command make
return
fi
done
echo build/soong/soong_ui.bash --make-mode
else
echo command make
fi
}
step 3. 執行構建指令,並列印構建時間、構建結果
@LINUX\android\build\envsetup.sh
function _wrap_build()
{
...
local start_time=$(date +"%s")
"$@" //執行構建指令
local ret=$?
local end_time=$(date +"%s")
local tdiff=$(($end_time-$start_time))
local hours=$(($tdiff / 3600 ))
local mins=$((($tdiff % 3600) / 60))
local secs=$(($tdiff % 60))
local ncolors=$(tput colors 2>/dev/null)
...
echo
if [ $ret -eq 0 ] ; then
echo -n "${color_success}#### build completed successfully " //列印構建成功結果
else
echo -n "${color_failed}#### failed to build some targets " //列印構建失敗結果
fi
if [ $hours -gt 0 ] ; then
printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs //列印構建時間
...
return $ret
}
構建指令如下:
高通平臺指令:
build/soong/soong_ui.bash --make-mode dist -j32 ENABLE_AB=true SYSTEMEXT_SEPARATE_PARTITION_ENABLE=true BOARD_DYNAMIC_PARTITION_ENABLE=true ENABLE_VIRTUAL_AB=false SHIPPING_API_LEVEL=29
5.4.3 goLang腳本部分
5.4.3.1 執行soong_ui.bash腳本
soong_ui.bash腳本主要做了兩件事:
- 根據"android/soong/cmd/soong_ui/"內容,生成soong_ui的go可執行程式,生成路徑:out\soong_ui
- 執行soong_ui程式,進入soong_ui世界
@build\soong\soong_ui.bash
...
# Save the current PWD for use in soong_ui
export ORIGINAL_PWD=${PWD}
export TOP=$(gettop)
source ${TOP}/build/soong/scripts/microfactory.bash
soong_build_go soong_ui android/soong/cmd/soong_ui//構建soong_ui執行程式
cd ${TOP}
exec "$(getoutdir)/soong_ui" "$@"//執行soong_ui程式,啟動構建
5.4.3.2 soong入口
soong_ui是個go程式,至此進入soong構建系統的世界。
@android\build\soong\cmd\soong_ui\main.go
func main() {
...
c, args := getCommand(os.Args)
...
f := build.NewSourceFinder(buildCtx, config)
defer f.Shutdown()
build.FindSources(buildCtx, config, f)//遍歷整個項目,記錄所有的mk、bp等文件
c.run(buildCtx, config, args, logsDir)//啟動構建
}
5.4.3.3 soong構建系統
soong構建系統最核心的步驟。其主要通過將bp、mk文件,解析成ninja文件,再通過ninja去實現系統構建任務。
@android\build\soong\ui\build\build.go
func Build(ctx Context, config Config, what int) {
...
runSoong(ctx, config)//step 1.處理bp文件
...
runKatiBuild(ctx, config)//step 2.處理mk文件
...
createCombinedBuildNinjaFile(ctx, config)//step 3.整合ninja文件
...
runNinja(ctx, config)//step 4.構建
...
}
step 1. runSoong
runSoong 對工具進行編譯,先編譯出blueprint等編譯工具, 再把*.bp 編譯成 out/soong/build.ninja。
@android\build\soong\ui\build\soong.go
func runSoong(ctx Context, config Config) {
...
ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")//1.主要用於生成out/soong/.minibootstrap目錄相關文件
...
ctx.BeginTrace(metrics.RunSoong, "environment check")//2.初始環境檢查
...
ctx.BeginTrace(metrics.RunSoong, "minibp")//3.創建minibp可執行程式
...
ctx.BeginTrace(metrics.RunSoong, "bpglob")//4.創建bpglob可執行程式
...
ninja("minibootstrap", ".minibootstrap/build.ninja")//5.主要用於生成out/soong/.bootstrap/build.ninja文件
ninja("bootstrap", ".bootstrap/build.ninja")//6.生成out/soong/build.ninja
}
(1)創建 out/soong/.minibootstrap/目錄併在這個目錄下創建一系列文件,包括out/soong/.minibootstrap/build.ninja這個文件。該路徑下的內容,會參與到bootstrap階段的構建;
(2)檢查soong構建的環境、工具是否存在&正常;
(3)通過microfactory生成out/soong/.minibootstrap/minibp可執行程式,會參與到bootstrap階段的構建;
(4)通過microfactory生成out/soong/.minibootstrap/bpglob可執行程式,會參與到bootstrap階段的構建;
(5)通過步驟三編譯生成的minibp程式,生成out/soong/.bootstrap/build.ninja文件,該文件會參與到bootstrap階段的構建,可參考verbose.log列印的日誌:
@android\out\verbose.log
[1/1] out/soong/.minibootstrap/minibp -t -l out/.module_paths/Android.bp.list -b out/soong -n out -d out/soong/.bootstrap/build.ninja.d -globFile out/soong/.minibootstrap/build-globs.ninja -o out/soong/.bootstrap/build.ninja Android.bp
(6)bootstrap表示從無到有創建Soong,該階段會先生成bootstrap相關的工具程式:./out/soong/.bootstrap/bin/*,再使用編譯生成的soong_build程式,生成out/soong/build.ninja文件。用於後續參與Ninja編譯構建工作,可參考verbose.log列印的日誌:
@android\out\verbose.log
[2/2] out/soong/.bootstrap/bin/soong_build -t -l out/.module_paths/Android.bp.list -b out/soong -n out -d out/soong/build.ninja.d -globFile out/soong/.bootstrap/build-globs.ninja -o out/soong/build.ninja Android.bp
out/soong/build.ninja文件羅列了項目上所有的bp模塊編譯規則,及其相關依賴模塊、SDK、簽名信息、臨時文件等(文件很大,約3.53G,慎重打開)。如下為該文件部分內容:
(7)該階段在編譯時,控制台列印的log如下:
step 2. runKatiBuild
runKatiBuild, 載入 build/make/core/main.mk, 搜集所有的Android.mk文件生成out/build-xxx.ninja文件
@android\build\soong\ui\build\kati.go
func runKatiBuild(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunKati, "kati build")
...
args := []string{
"--writable", config.OutDir() + "/",
"-f", "build/make/core/main.mk",
}
...
runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})//執行ckati指令,構建mk
...
}
(1)參考soong.log的日誌,runKati函數最終會引用cKati指令,載入main.mk文件,生成ninja文件,其指令如下:
@android\out\soong.log
2023/09/08 10:47:50.479597 build/soong/ui/build/exec.go:60: prebuilts/build-tools/linux-x86/bin/ckati [prebuilts/build-tools/linux-x86/bin/ckati --ninja --ninja_dir=out --ninja_suffix=-qssi --no_ninja_prelude --regen --ignore_optional_include=out/%.P --detect_android_echo --color_warnings --gen_all_targets --use_find_emulator --werror_find_emulator --no_builtin_rules --werror_suffix_rules --warn_real_to_phony --warn_phony_looks_real --werror_real_to_phony --werror_phony_looks_real --werror_writable --top_level_phony --kati_stats --writable out/
-f build/make/core/main.mk --werror_implicit_rules SOONG_MAKEVARS_MK=out/soong/make_vars-qssi.mk SOONG_ANDROID_MK=out/soong/Android-qssi.mk TARGET_DEVICE_DIR=device/qcom/qssi KATI_PACKAGE_MK_DIR=out/target/product/qssi/obj/CONFIG/kati_packaging]
(2)build/make/core/main.mk又是何方神聖?從main.mk開始,將通過include命令將其所有需要的.mk文件包含進來,最終在記憶體中形成一個包括所有編譯腳本的集合,這個相當於一個巨大Makefile文件。網上有個圖,可以很好的描述其關係,如下:
文件 | 說明 |
---|---|
build/make/core/main.mk | Build的主控文件,主要作用是包含其他mk,以及定義幾個最重要的編譯目標,同時檢查編譯工具的版本,例如gcc、clang、java等 |
build/make/core/config.mk | Build的配置文件,主要是區分各個產品的配置,並將這些編譯器參數引入產品配置 BoardConfig.mk,同時也配置了一些編譯器的路徑等 |
build/make/core/clang/config.mk | clang編譯的配置文件 |
build/make/core/definitions.mk | 最重要的 Make 文件之一,在其中定義了大量的函數。這些函數都是 Build 系統的其他文件將用到的。例如:my-dir,all-subdir-makefiles,find-subdir-files,sign-package 等,關於這些函數的說明請參見每個函數的代碼註釋。 |
build/make/core/dex_preopt.mk | 定義了dex優化相關的路徑和參數 |
build/make/core/pdk_config.mk | 編譯pdk的配置文件 |
build/make/core/Makefile | 系統最終編譯完成所需要的各種目標和規則 |
build/make/core/envsetup.mk | 包含進product_config.mk文件並且根據其內容設置編譯產品所需要的環境變數,並檢查合法性,指定輸出路徑等 |
build/make/core/combo/select.mk | 根據當前編譯器的平臺選擇平臺相關的 Make 文件 |
build/make/core/ninja_config.mk | 解析makefile的的列表,傳給kati,配置傳給ninja和kati的目標 |
build/make/core/soong_config.mk | 配置soong的環境變數,建立go變數和mk變數的json映射關係,讓go變數可以獲取到mk中定義的變數值 |
(3)如上關係圖所示,mk文件索引的幾乎都是config.mk之類的配置文件,那我們編譯模塊對應的Android.mk又在哪個位置被引入呢?在5.4.3.2節,在soong啟動時,我們便會去搜索項目中所有的Android.mk文件,並記錄於out/.module_paths/Android.mk.list文件。在main.mk裡面,便可以根據這個文件,將所有的內容include進來。因此,在該項目下定義的任一Android.mk都可以被引用。
@android/build/make/core/main.mk
...
subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list) $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk
subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
.KATI_READONLY := subdir_makefiles_total
//遍歷相關mk文件
$(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
...
(4)main.mk載入完成後,最終生成out/build-xxx.ninja文件,用於後續參與Ninja編譯構建工作。out/build-xxx.ninja文件羅列了項目上所有的mk模塊編譯規則,及其相關依賴模塊、SDK、簽名信息、臨時文件等(qssi模塊和target模塊,約1G+1.08G,文件較大,慎重打開)。
(5)該階段在編譯時,控制台列印的log如下:
step 3. createCombinedBuildNinjaFile
為了方便統一管理,Soong將out/soong/build.ninja文件 、out/build-*.ninja文件和out/build-*-package.ninja文件, 合成為out/combined-*.ninja文件,由該文件記錄所有待執行ninja文件。
@android\build\soong\ui\build\build.go
var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
builddir = {{.OutDir}}
{{if .UseRemoteBuild }}pool local_pool
depth = {{.Parallel}}
{{end -}}
pool highmem_pool
depth = {{.HighmemParallel}}
build _kati_always_build_: phony
{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}//追加文件out/build-*.ninja
subninja {{.KatiPackageNinjaFile}}//追加文件out/build-*-package.ninja
{{end -}}
subninja {{.SoongNinjaFile}}//追加文件out/soong/build.ninja
`))
func createCombinedBuildNinjaFile(ctx Context, config Config) {
...
file, err := os.Create(config.CombinedNinjaFile())//創建combined-*.ninja文件
...
if err := combinedBuildNinjaTemplate.Execute(file, config); //執行合併動作
...
}
out/combined-qssi.ninja文件,如下:
step 4. runNinja
runNinja,運行Ninja命令, 解析combined-*.ninja,執行編譯過程
@android\build\soong\ui\build\ninja.go
func runNinja(ctx Context, config Config) {
...
executable := config.PrebuiltBuildTool("ninja")//獲取ninja指令
args := []string{
"-d", "keepdepfile",
"-d", "keeprsp",
"--frontend_file", fifo,
}
...
args = append(args, "-f", config.CombinedNinjaFile())//配置組合的ninja文件
args = append(args,
"-w", "dupbuild=err",
"-w", "missingdepfile=err"
cmd := Command(ctx, config, "ninja", executable, args...)//初始化ninja指令參數
...
ctx.Status.Status("Starting ninja...")
cmd.RunAndStreamOrFatal()//執行ninja指令
}
(1)參考soong.log的日誌,runNinja函數最終會引用ninja指令,載入out/combined-*.ninja文件,執行最終的編譯,其指令如下:
@android\out\soong.log
2023/09/12 14:13:05.709769 build/soong/ui/build/exec.go:60: prebuilts/build-tools/linux-x86/bin/ninja [prebuilts/build-tools/linux-x86/bin/ninja -d keepdepfile -d keeprsp --frontend_file out/.ninja_fifo droid -j 34 -f out/combined-bengal.ninja -w dupbuild=err -w missingdepfile=err]
(2)該階段在編譯時,控制台列印的log如下:
5.4.3.4 soong編譯創建文件
文件 | 備註 |
---|---|
android/out/soong.log | soong模塊列印內容 |
android/out/verbose.log | 控制台編譯日誌 |
android/out/dumpvars-verbose.log | lunch的log信息 |
android/out/.ninja_log | ninja模塊編譯log |
android/out/soong_ui | go可執行程式,執行soong編譯 |
android/out/.module_paths/ | 遍歷整個項目,記錄所有的mk、bp等文件 |
android/out/soong/build.ninja | 項目上所有bp模塊的編譯規則 |
android/out/build-*.ninja | 項目上所有mk模塊的編譯規則 |
android/out/combined-*.ninja | 項目上所有模塊的編譯規則組合 |
android/out/soong/host/linux-x86/bin/androidmk | mk文件轉bp文件的指令 |
六、編譯小竅門
6.1 模塊編譯速度優化
隨著Android版本的更迭,尤其是Androd10和Android11以上源碼的編譯,單編模塊的時間也特別慢,每次都需要小半個小時甚至更長,因為每次單編都會重新載入所有mk文件,再生成ninja編譯,此過程很慢,實際編譯過程很快。
(1)如下指令是AOSP的快編指令,用於快速編譯單個模塊:
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja SnapdragonCamera -j32
格式 | 描述 |
---|---|
./prebuilts/build-tools/linux-x86/bin/ninja | 指定了要使用的構建工具,即Ninja構建系統。該指令通過在AOSP預構建工具鏈目錄下找到Ninja可執行文件進行調用。 |
out/combined-aosp_arm-eng.ninja | 指定了Ninja構建系統要使用的構建文件的路徑和名稱。在AOSP編譯過程中,生成的構建文件會存儲在out目錄中,且命名通常包含目標設備的相關信息。 |
SnapdragonCamera | 指定了要構建的目標模塊或子模塊的名稱。 |
-j32 | 指定了併發構建的線程數。-j32表示同時使用32個線程進行構建,以加快構建速度。 |
其他常用的指令,如:
#編譯Settings
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja Settings -j32
#編譯selinux
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja selinux_policy -j32
#編譯Framework
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja frameworks -j32
#全編譯
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja -j32 2>&1 |tee ninja_build.log
(2)ninja與mm指令對比,查看編譯速度(工程已經編譯過,刪除SnapdragonCamera目錄,比較編譯完成時間)
指令 | 編譯時間 (mm:ss) | 編譯時間 (mm:ss) | 編譯時間 (mm:ss) |
---|---|---|---|
mm SnapdragonCamera | 05:58 | 05:37 | 04:25 |
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja SnapdragonCamera -j32 | 00:05 | 00:04 | 00:04 |
6.2 Android.mk轉換為Android.bp
Android.bp的出現就是為了替換Android.mk文件。bp跟mk文件不同,它是純粹的配置,沒有分支、迴圈等流程式控制制,不能做算數邏輯運算。如果需要控制邏輯,那麼只能通過Go語言編寫。舊的mk可以轉換為bp,Soong會編譯生成一個androidmk命令,用於將Android.mk文件轉換為Android.bp文件。
(1)生成androidmk文件
確認out/soong/host/linux-x86/bin/目錄下是否存在androidmk文件,如不存在androidmk文件,使用如下命令生成:
source build/envsetup.sh
m -j blueprint_tools
(2)通過androidmk執行轉化動作
cd out/soong/host/linux-x86/bin/
androidmk android.mk > android.bp
(3)轉換前的android.mk
預置應用到vendor分區
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := TCP_UDP
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)
LOCAL_SRC_FILES := app/TCP_UDP.apk
include $(BUILD_PREBUILT)
(4)轉換後的android.bp
轉化對應的規則,可參考:build/soong/androidmk/androidmk/android.go
android_app_import {
name: "TCP_UDP",
local_module_path: {
var: "TARGET_OUT_VENDOR",
},
apk: "app/TCP_UDP.apk",
presigned: true,
}
七、小結
- Android的Treble架構;
- Soong構建流程;
- Out路徑下關於構建生成的臨時文件、日誌;
- Blueprint、Kati、Ninja模塊的作用;
八、參考資料
Treble架構:
https://segmentfault.com/a/1190000021550665?sort=newest
Android-Make指令:
https://blog.csdn.net/yiranfeng/article/details/109084082
https://blog.csdn.net/yiranfeng/article/details/109148537
https://zhuanlan.zhihu.com/p/342303212
https://zhuanlan.zhihu.com/p/342817768
https://blog.csdn.net/m0_37624402/article/details/91409900
編譯優化:
https://blog.csdn.net/lontano_0406/article/details/131162119
http://wed.xjx100.cn/news/268761.html?action=onClick
https://blog.csdn.net/weixin_36389889/article/details/128469488