Android 編譯介紹

2023-09-13 18:01:11

一、需求

        Android的原始碼非常的龐大,編譯Android系統往往會佔用我們很長的時間,我們需要了解下Android的編譯規則,以期能提高我們的開發效率,達到程式設計師按時下班的偉大理想。

二、環境

  1. 平臺:QCM2290
  2. 版本:Android 11
  3. 編譯伺服器: 64G + 32核

三、相關概念

3.1 Treble架構

        由於Android各個層級之間的耦合性大,Android系統更新成本高,導致Android系統版本雖然已經釋放了很久,但是市面上Android的系統依然存在滯後、碎片化的情況。Android 8.0 重新設計了 Android 作業系統框架(名為「Treble」的專案),以便讓製造商能夠以更低的成本更輕鬆、更快速地將裝置更新到新版 Android 系統。
        Android 7.x 及更早版本中沒有正式的供應商介面。當裝置製造上升級Android系統時,需要移植大量的程式碼。因為Framework與供應商程式碼打包在一個映象中,所以必須整體升級。

        Android 8.0 及更高版本提供了一個穩定的新供應商介面。裝置製造商存取的是 Android 程式碼中特定於硬體的部分,而不會依賴供應商的私有介面。並且供應商會使用獨立的vendor分割區。這樣,裝置製造商在更新 Android 作業系統框架,只需要單獨升級system分割區。同時穩定的供應商介面會保證相容性。
        Treble的目標是將供應商實現(大部分有晶片製造商編寫特定裝置軟體和底層軟體)與Android作業系統框架分隔開來,這通過供應商介面來實現。為了確保供應商實現的向前相容性,供應商介面會由供應商測試套件 (VTS) 進行驗證,該套件類似於相容性測試套件 (CTS)。您可以使用 VTS 在舊版 Android 架構和當前 Android 架構中自動執行 HAL 和作業系統核心測試。

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指令碼主要做了兩件事:

  1. 根據"android/soong/cmd/soong_ui/"內容,生成soong_ui的go可執行程式,生成路徑:out\soong_ui
  2. 執行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,
}

七、小結

  1. Android的Treble架構;
  2. Soong構建流程;
  3. Out路徑下關於構建生成的臨時檔案、紀錄檔;
  4. 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