---
title: 'Android 下載源碼、編譯系統'
disqus: kyleAlien
---
Android 下載源碼、編譯系統
===
## OverView of Content
如有引用該文章請標明引用,謝謝 :smile_cat:
以下是在 Ubuntu 20 進行
[TOC]
## Android Source
1. 個人使用 Ubuntu (`22.0.4` 版本)
2. 先使用 apt 安裝以下工具
```shell=
sudo apt upgrade
sudo apt update
sudo apt install -y \
vim \
git-core \
gnupg \
flex \
bison \
gperf \
build-essential \
zip \
unzip \
curl \
zlib1g-dev \
gcc-multilib \
g++-multilib \
libc6-dev-i386 \
lib32ncurses5-dev \
x11proto-core-dev \
libx11-dev \
lib32z-dev \
ccache \
libgl1-mesa-dev \
libxml2-utils xsltproc \
libncurses5 \
libelf-dev \
libswitch-perl
```
3. **安裝 google's repo 工具**來下載 Android Source、Android Kernel code
:::info
* **確認 Python**,如果沒安裝請按照以下步驟先進行安裝
```shell=
# 必須使用 python3 (2 已經不再支援)
sudo apt install python3
# 創建 symbol 連結為 python (因為 repo 是使用 python 關鍵字)
sudo ln -s /usr/bin/python3 /usr/bin/python
```
:::
* 下載安裝 repo 工具
```shell=
# 定義 REPO 變量
export REPO=$(mktemp /tmp/repo.XXXXXXXXX)
# 下載指定 Url 到 REPO 中
curl -o ${REPO} https://storage.googleapis.com/git-repo-downloads/repo
# gpg Key
gpg --recv-key 8BB9AD793E8E6153AF0F9A4416530D5E920F5C65
# 創建 /bin 資料夾
mkdir ~/bin
# 下載 repo 到 /bin 資料夾中
curl -s https://storage.googleapis.com/git-repo-downloads/repo.asc | gpg --verify - ${REPO} && install -m 755 ${REPO} ~/bin/repo
```
:::warning
* `gpg` 驗證失敗 couldn't connect
```shell=
gpg: requesting key 920F5C65 from hkp server keys.gnupg.net
?: keys.gnupg.net: Host not found
gpgkeys: HTTP fetch error 7: couldn\'t connect: Success
gpg: no valid OpenPGP data found.
gpg: Total number processed: 0
```
其中可以替過替換金鑰伺服器來修復
```shell=
gpg --keyserver hkp://keyserver.ubuntu.com --recv-key 8BB9AD793E8E6153AF0F9A4416530D5E920F5C65
```
:::
* 添加到當前 User 的環境變數
```shell=
vim ~/.bashrc
# 添加內容如下 --------------------------------------------------
export PATH=$PATH:/home/alien/bin
# ---------------------------------
## 編輯完後刷新 `.bashrc`
source ~/.bashrc
```
* 使用以下指令確認 repo 是否安裝成功
```shell=
# 檢查 repo 版本
repo version
```
> 
### 下載 [Android Source](https://source.android.com/setup/build/downloading)
1. **創建 source 資料夾**:這個資料夾用來儲存 Android Source code
```shell=
mkdir android_source && cd android_source
```
:::info
* 確認有設定 git 的基本資訊,`name`、`email`
```shell=
git config --global user.name <Your Name>
git config --global user.email <you@example.com>
```
:::
2. `repo init`:指定你要下載的 url & branch ([**branch name**](https://source.android.com/setup/start/build-numbers#source-code-tags-and-builds) 可以參考官方文檔)
```shell=
## 這裡下載 android-12.0.0_r1 版本
repo init -u https://android.googlesource.com/platform/manifest \
-b android-12.0.0_r1
## 還有 android-10.0.0_r1 版本
repo init -u https://android.googlesource.com/platform/manifest \
-b android-10.0.0_r1
```
> 產生 `.repo` 目錄
>
> 
:::success
* 可以透過查看 `.repo/manifests/default.xml` 檔案來查看當前的設置
```shell=
cat .repo/manifests/default.xml | head -n 10
```
> 
:::
3. `repo sync`:sync 關鍵字就是開始下載
```shell=
repo sync
## 如果想加速則可以指定 -j (線程使用數量)、-c、-q (安靜模式)
repo sync -c -j12 ## 使用 12 核下載
```
* 下載完後接近 192G,資料相當龐大~ 請分配好空間 (Build 後會更耗費空間)
> 
### 編譯 Android Source
:::info
建議使用真的 Ubuntu 編譯,我使用 WSL's Ubuntu 編譯出來的 emulator 不能正常使用
:::
:::danger
* 如果是使用 `Window WSL ubuntu` 系統,請注意容量分配,一般來說預設容量是不足的,所以必須將 ubuntu 擴容 (自身預設為 250G,這裡我將它擴容到 512G),[**官方擴容文檔**](https://docs.microsoft.com/zh-tw/windows/wsl/vhd-size)
以下操作請使用 **系統管理員身分** 開啟 `powershell`
1. 查看 WSL ubuntu 的硬碟區塊
```shell=
Get-AppxPackage -Name "*Ubuntu*" | Select PackageFamilyName
```
> 
2. 找到 `pathToVHD` 完整路徑:
```shell=
## 路徑格式
%LOCALAPPDATA%\Packages\<PackageFamilyName>\LocalState\<disk>.vhdx
## <PackageFamilyName> 就是我們第一步驟中所查的目標
## <disk> 則是 ubuntu 分配的 disk
```
3. 在 disk 模式下指定 ubuntu disk
```shell=
## 進入 disk 模式
diskpart
## 在該模式下選定要操作的硬碟區塊
## <pathToVHD> 則是步驟 2 中的 "完全路徑"
Select vdisk file="<pathToVHD>"
## 查看該硬碟的分配狀況
detail vdisk
## 拓展大小為 512M
expand vdisk maximum=512000
## 退出 disk 模式
exit
```
4. ubuntu 拓展:雖然上面 window 已經分配更大空間給 ubuntu,但 ubuntu 仍需重新指定硬碟大小
```shell=
## 查看當前硬碟
sudo mount -t devtmpfs none /dev
mount | grep ext4
## 重新指定大小 512M
## 以下的 /dev/sd<字母> 是透過上面查詢出來的
sudo resize2fs /dev/sdc 512M
```
:::
1. **初始化環境**:在 Android 下載好的根資料夾下達以下命令
```shell=
## 移動到下載的資料夾內
cd ~/android_source
source ./build/envsetup.sh
make clobber ## 編譯前清除 build 資料夾
```
2. **lunch 選擇編譯目標**:選擇要編譯的目標,**格式組成是 `BUILD`、`BUILDTYPE`**
* Build 是指編譯完後要運行的平台
| BUILD | 目標設備 | 補充 |
| -------- | -------- | -------- |
| Full | 模擬器 | 全編譯,包含所有語言、應用、輸入法... 等等 |
| full_manguro | manguro | 全編譯,運行於 Galaxy Nexus GSM/HSPA+("manguro") |
| full_panda | panda | 全編譯,運行於 PandaBoard("panda") |
* BUILDTYPE 則分為三種類型
| BUILDTYPE | 功能特點 |
| -------- | -------- |
| user | 發佈到市場上的版本,**預設沒有 root 權限、adb 關閉** |
| userdebug | **開放 root、adb 權限**,一般用於真機調適 |
| eng | 擁有最大權限 (**root**),並具有額外調適工具,**一般用於模擬器** |
> 有再細分為 `x86`、`x86_64`... 等等
```shell=
# 查看所有可編譯的版本類型
lunch
lunch 39 # 選擇 `aosp_x86-eng` 版本
```
> 
3. `make` 編譯:可以按照自己電腦的 CPU 核心數量來編譯
```shell=
# 目前指定 12 核心 (全力編譯)
# make -j$(nproc)
make -j12
```
> 
:::success
* **`sdk_phone` 的選項**
Android 12 的 lunch 沒有輸出 `sdk_phone` 的選項,但這部分仍可以在 `build/target/product` 資料夾那找到,所以仍可以指定
> 
```shell=
## 隱藏選項
lunch sdk_phone_x86
## repo sync -c -j12
make -j$(nproc --all)
```
> 
:::
### Android Source 單編譯 - 模組
* 除了全部編譯以外,我們可以使用單單編譯某一個應用模塊 (eg. `Settings` 應用模塊)
1. 在 Android Source 根目錄中,執行以下命令
```shell=
source ./build/envsetup.sh
lunch 39
```
2. 進入 Setting 目錄下,使用 `mm` 編譯
```shell=
## 模塊編譯 (目前編譯 Settings 模塊)
mmm packages/apps/Settings
```
除了 `mm` 指令以外,還可以使用其他命令單編譯
| 指令 | 功能 |
| -------- | -------- |
| mm | 編譯當前目錄下 Module(必須進入指定工程的目錄中才能編譯) |
| mmm | 編譯指定目錄下 Module,**但不編譯其依賴的 Module** (可以在 Android 源代碼的目錄結構中的任意級目錄,編譯任意指定的工程) |
| mma | 編譯當前目錄下 Module,**包含其依賴的 Module** |
| mmma | 編譯 **指定路徑下的 ++所有 Module++**,並包含其依賴 |
> 
:::info
* 在執行完 `source ./build/envsetup` 命令後,就可以使用 `help` 指令查看還有哪些命令可用
:::
* 範例:**單編譯 Setting 模塊後的輸出**
> 
使用編譯好的 apk 有兩種方式
1. `adb push`、`adb install` 命令安裝 APK
2. 使用 `make snod` 命令,生成新的 `system.img`,然後運行模擬器
### 下載 [Android Kernel](https://source.android.com/setup/build/building-kernels#downloading)
* **Android Kernel 並不存在 Android Source 中**,需要另外下載 (同樣 **可以使用 repo 工具**)
:::warning
* 選擇的內核版本要選用與模擬器版本盡量一致(否則可能模擬器無法運行)
像是 Pixel 3a XL 的核心就要使用 `android-msm-bonito-4.9-android12L` 核心
> 
:::
1. **創建 kernel 資料夾**:這個資料夾用來儲存 Android Kernel code
```shell=
mkdir android_kernel && cd android_kernel
```
2. `repo init`:指定你要下載的 url & branch ([**branch name**](https://source.android.com/setup/start/build-numbers#source-code-tags-and-builds) 可以參考官方文檔、[**Android source Manifest 文件**](https://android.googlesource.com/kernel/goldfish/+refs)、[**Kernel 文件**](https://source.android.com/docs/setup/build/building-kernels#downloading))
```shell=
## 這裡下載 android-gs-raviole-5.10-android12L 版本
# (其他版本請看官網提供)
repo init -u https://android.googlesource.com/kernel/manifest \
-b android-gs-raviole-5.10-android12L
```
> 產生 `.repo` 目錄
>
> 
3. `repo sync`:sync 關鍵字就是開始下載
```shell=
repo sync
## 如果想加速則可以指定 -j (線程使用數量)、-c、-q (安靜模式)
repo sync -c -j12 ## 使用 12 核下載
```
> 
### 編譯 Android Kernel
* 首先 apt 安裝 tool 編譯 Android kernel 時必須的一些工具
```shell=
## 多安裝 openssl tool
sudo apt install libssl-dev
```
* 進到 Android Kernel 下載好的資料夾根目錄中,並執行以下命令
```shell=
## 進入資料夾
cd ~/android_kernel
## 編譯命令
./build/build.sh
```
最終編譯結果
> 
### 下載並編譯 Emulator Kernel - 手動指定並編譯
* Android Kernel 版本的不同
* 在 Android 10 (`Version Q`, `Kernel 4.14`) 之前 Android Kernel 把所有核心 `built-in` 到 **bzImage** 檔案中
* 從 Android 11 (`Version R`) 之後 (包含),為了硬體需求,**將部分核心驅動編譯到 `System Image` 中**
* 編譯範例、順序:
下載 goldfish 版本編譯、並指定給模擬器作為核心
1. **`clone goldfish`** (Android Emulator 使用的 Kernel)
```shell=
git clone https://android.googlesource.com/kernel/goldfish/ -b android-goldfish-4.14-dev.150
```
2. **`clone gcc` 編譯工具**
```shell=
git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9 -b android10-release
```
3. **將 gcc 編譯工具,加入臨時路徑到 Path 中**
```shell=
export PATH=$PATH:$PWD/x86_64-linux-android-4.9/bin
```
4. **設定編譯參數 `ARCH`、`CROSS_COMPILE`,並執行 make 編譯**
```shell=
# 進入 kernel source
cd goldfish
make clean
make mrproper
# default setting(.config file)
make ARCH=x86_64 x86_64_ranchu_defconfig
make ARCH=x86_64 -j$(nproc --all)
# 也可以手動設定 CROSS_COMPILE
#
## make -j$(nproc --all) ARCH=x86_64 CROSS_COMPILE=x86_64-linux-android-
```
:::warning
* 編譯錯誤 **multiple definition of `yylloc`**,這是因為電腦中有多個 gcc,但是目前編譯的 Kernel 並不能使用,這時須將 gcc 調整為 gcc9
```shell=
sudo apt install gcc-9
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 100
```
* `Can't find default configuration x86_64_ranchu_defconfig` 錯誤?
* 每個 goldfish 版本的 Config 設定都也些許不同,請查看 `<android_kernel>/arch/<目標_cpu>/` 的目錄下有哪些設定
> 像是 goldfish 3.4 版本就沒有這個設定檔
>
> 
* goldfish 模擬器則可以指定 `goldfish_armv7_defconfig`
* `Cannot use CONFIG_CC_STACKPROTECTOR_STRONG` 錯誤:因為編譯本身不支援,所以要先 disable
```shell=
scripts/config --disable CC_STACKPROTECTOR_STRONG
```
:::
* 運行模擬器,並設定指定 Kernel
```shell=
# 當前使用 Android SDK 的 emulator
emulator -avd Pixel_3_XL_API_29 -kernel ~/Desktop/android_kernel/goldfish/arch/x86_64/boot/bzImage &
```
:::danger
* 由於這裡下載 `goldfish-4.14` 版本,所以最高就使用 Android 10 來運行,超過 Android 10 的版本就無法正常運行 !!
:::
驗證 Android Emulator 是否是正確 Kernel
```shell=
adb shell
cat /proc/version
```
* **Original Kernel Version**
> 
* **Custome Kernel Version**
> 
### 模擬器運行
* 如果你已經手動編譯完成,就不需要指定,直接在 Android source root 根資料夾下使用以下指令即可
> 如果你尚未編譯完,是無法直接使用 `emulator` 命令的
```shell=
source ./build/envsetup.sh
lunch 39
emulator
```
> 
:::warning
* 無法在 ViertualBox 運行 (結果好像還是不行...)
由於這裡是使用 Oracle VM VirtualBox 運行 Ubuntu,運行模擬器需要虛擬化技術,這裡就需要 **設定巢狀虛擬化技術**
1. 先關閉 Ubuntu VM 系統
2. Window cmd 移動到 `C:\Program Files\Oracle\VirtualBox`,並執行以下命令
```shell=
## 查詢 VM 系統名
VBoxManage.exe list vms
## 指定系統名,開啟虛擬化
VBoxManage.exe modifyvm "Ubuntu" --nested-hw-virt on
```
> 
:::
:::danger
* **缺失 `userdata-qemu.img`**:
這是缺失設備 SDK 導致 ! 切換到 lunch 編輯指定 sdk 版本,並重新指定下載、編譯
> 
```shell=
## 切換到 lunch 66
lunch 66
repo sync -c -j12
make -j12
```
> 
:::
## Makefile
請看另外一篇 [**make 編譯管理**](https://hackmd.io/KrGjNjsGQeODDRZeCBqlRg)
## Android 編譯系統
Google 在 2006 年收購 Android 後有對編譯系統重新大整理一次,核心目標是 **==提升編譯效率==**
* 讓依賴關係更可靠,以保證可以正確判斷出需要被編譯的模塊
* 不必被編譯的模塊也可以被判斷出來
### 特色
Android 遵循了多個設計原則 & 策略,包括但不限於以下幾點
1. 同一套程式編譯出不同的建構目標,就像是 Linux 可以編譯出 Window 專用的模擬器 or 開發包等等,也稱為 **交叉編譯**
2. Non-Recursive Make:1997 年論文<<Recursive Make Considered Harmful\>>,其思想是 **在大型項目中應採用唯一的 Makefile 來組織所有文件的自動化編譯**
3. 可以單獨對 **++單一模塊進行驗證++**,不需要每次編譯全部項目
4. 編譯所產生的 ++中間文件++ & ++最終的編譯結果++ & ++Source Code++ 分離在不同的目錄上
### Makefile 依賴樹 - 概念
* 在前面小節有示範如何使用 Makefile (SimpleMakefile),現在來看看這個範例**每個 TARGET 的依賴關係** (箭頭表示了依賴的對象)
> 
由這張圖可以看到,由上而下的分析方式,Android 編譯系統也可以使用相同的方式分析
### Android 編譯系統抽象模型
* 因為是基於 Makefile 實現,整個編譯系統的核心仍是如何建構出有效的依賴樹
> 
1. **初始化環境**:由於 Android 編譯過程涉及 Java、C/C++ 等等多種語言,而且還有分 Host & Target 平台,所以它的運行環境稍微複雜,需要我們在初始化環節做好環境搭建 (e.g 當前 DSK & Make 版本是否符合需求)
2. **依賴樹、編譯**:編譯的執行和傳統 Make 並無太多差異
3. **打包**:編譯系統的另外一個重要任務就是打包 (e.g `system.img`、`boot.img`、`userdata.img`...)
### Root node - droid 依賴樹
* 根結點也就是 Makefile 的 Target,也是編譯系統最終要產生的目標,但它是一個 **==偽目標==**,因為在編譯過程產生的其他檔案也是不同的 Targets
從 Android Source 的根目錄分析起,其下的 **Makefile 文件是編譯系統的起點(`build/core/Makefile`)**,它是一個簡單的文件轉向,直接引用了另一個 Makefile 文件
```shell=
# 這邊在 Source 沒有找到,在書本找到而已
include build/core/main.mk
```
* 這邊需要注意 **兩點**
1. 編譯系統中往往 **++不只一顆依賴樹++** (Android 系統下 make、make sdk 編譯的結果就不相同),沒有指定編譯目標時,第一個會被默認為 Make 的根結點
2. Make 程序會對 Makefile 中的內容進行順序解析,解析的過程有三個大步驟,
* 變量賦值 & 環境檢測 & 初始化
* 按照規則生成依賴樹
* 根據用戶選擇的依賴樹從 **++葉到根++** 生成目標文件 (類似先有零件,在組成目標配備)
:::warning
* 如果源碼沒有調整,再次編譯,就會返回 `Nothing to be done for droid'`;不代表編譯失敗
> 
:::
* 接下來看看 [**main.mk**](https://android.googlesource.com/platform/build/+/master/core/main.mk),找出 `make` 命令對應的依賴樹的根結點,其 **根結點就是 ==droid==**
```shell=
## build/core/main.mk 檔案
## This is the default target. It must be the first declared target.
.PHONY: droid
DEFAULT_GOAL := droid
$(DEFAULT_GOAL): droid_targets
```
**這裡的 droid 還是一個空殼**,相當於預先佔位,但實作部分會分為其他不同的路線
```shell=
# 1 判斷是否是 ANDROID_BUILD_EVERYTHING_BY_DEFAULT
ifeq (true,$(ANDROID_BUILD_EVERYTHING_BY_DEFAULT))
droid: checkbuild
# 2 執行條件
.PHONY: checkbuild # 以下有三個條件
checkbuild: $(modules_to_check) droid_targets check-elf-files
# 3
.PHONY: apps_only
ifneq ($(TARGET_BUILD_APPS),) # 編譯 APP
# 3-1
apps_only: $(unbundled_build_modules)
droid_targets: apps_only
else ifeq (,$(TARGET_BUILD_UNBUNDLED)) # 編譯整個系統
# 3- 接下來會繼續分析 droidcore 產生
droid_targets: droidcore dist_files
```
* 接下來主要針對 **droidcore**、**dist_files** 進行分析
**--根依賴樹--**
> 
### droidcore 節點
* 在編譯整個 Android 系統時,droid(空殼)依賴於 **droidcore** & **dist_files**,這個小節先關注 **==droidcore== 的生成**
```shell=
# Build files and then package it into the rom formats
.PHONY: droidcore
droidcore: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \
$(INSTALLED_SYSTEMIMAGE_TARGET) \
$(INSTALLED_RAMDISK_TARGET) \
$(INSTALLED_BOOTIMAGE_TARGET) \
$(INSTALLED_RADIOIMAGE_TARGET) \
$(INSTALLED_DEBUG_RAMDISK_TARGET) \
$(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \
$(INSTALLED_RECOVERYIMAGE_TARGET) \
$(INSTALLED_VBMETAIMAGE_TARGET) \
$(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) \
$(INSTALLED_VBMETA_VENDORIMAGE_TARGET) \
... 省略部份 (還有很多 TARGET
$(INSTALLED_FILES_FILE_ROOT) \
$(INSTALLED_FILES_JSON_ROOT) \
$(INSTALLED_FILES_FILE_RECOVERY) \
$(INSTALLED_FILES_JSON_RECOVERY) \
$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
soong_docs
```
來看看幾個重點 Prerquisite
| Prerquisite | 描述 | 功能 |
| -------- | -------- | -------- |
| INSTALLED_BOOTIMAGE_TARGET | .PHONY: bootimage bootimage: $(INSTALLED_BOOTIMAGE_TARGET) | 生成 system.img |
| INSTALLED_RECOVERYIMAGE_TARGET | - | 生成 recovery.img |
| INSTALLED_CACHEIMAGE_TARGET | .PHONY: cacheimage cacheimage: $(INSTALLED_CACHEIMAGE_TARGET) | 生成 cache.img |
| INSTALLED_VENDORIMAGE_TARGET | .PHONY: vendorimage vendorimage: $(INSTALLED_VENDORIMAGE_TARGET) | 生成 verdor.img |
| INSTALLED_FILES_FILE | - | 生成 installed-files.txt,用於紀錄當前系統中預安裝的程序、Lib & Module |
* **modules_to_install**:描述了系統需要安裝的模塊,主要模塊有 **product_target_FILES**
```shell=
# /core/main.mk 檔案
# 1. All the droid stuff, in directories
.PHONY: files
files: $(modules_to_install) \
$(INSTALLED_ANDROID_INFO_TXT_TARGET)
# 2. 描述了系統需要安裝的模塊,分成了 5 個模塊
modules_to_install := $(sort \
$(ALL_DEFAULT_INSTALLED_MODULES) \
$(product_target_FILES) \
$(product_host_FILES) \
$(CUSTOM_MODULES) \
)
# 3. 分析 product_target_FILES 模塊
ifdef FULL_BUILD
product_target_FILES := $(call product-installed-files, $(INTERNAL_PRODUCT))
else
product_target_FILES :=
# 4. 分析 product-installed-files
# 在 Android 編譯機制中模塊可以指定 eng、debug、tests、asan、java_coverage
$(eval _pif_modules := \
$(call get-product-var,$(1),PRODUCT_PACKAGES) \
$(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \
$(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \
$(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \
$(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \
$(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \
$(call auto-included-modules) \
) \
... 省略部份
endef
# 5. tags_to_install,在這邊會過濾出需要安裝的模塊
ifeq ($(TARGET_BUILD_VARIANT),eng)
tags_to_install := debug eng
ifneq ($(filter ro.setupwizard.mode=ENABLED, $(call collapse-pairs, $(ADDITIONAL_SYSTEM_PROPERTIES))),)
# Don't require the setup wizard on eng builds
ADDITIONAL_SYSTEM_PROPERTIES := $(filter-out ro.setupwizard.mode=%,\
$(call collapse-pairs, $(ADDITIONAL_SYSTEM_PROPERTIES))) \
ro.setupwizard.mode=OPTIONAL
endif
```
### dist_file 節點
* 這是 Android 編譯系統時的另外一個節點,整個編譯項目指出現了一次,如果開啟這個開關,**其功能是在 out 目錄下產生專用的 dist 資料夾,用於儲存多種分發包(類似多渠道)**
```shell=
# dist_files only for putting your library into the dist directory with a full build.
.PHONY: dist_files
```
## [**main.mk**](https://android.googlesource.com/platform/frameworks/base/+/cd92588/Android.mk) 解析
* 在分析 droidcore、dist_files 之前,**先分析其架構**。除了建構 droid 等等依賴外,main 有一大半的內容是為了完成以下幾點
1. 對編譯環境的檢查
2. 必要文件的前期處理
3. 引用其他 MakeFile 檔案 (Source 中有多個 Makefile 檔案,在整個 main.mk 中可以看到許多引用)
> eg. config.mk、cleanbuild.mk
4. 設定全局變量
5. 函數實現 (Android 編譯系統中有時現許多有用的函數)
eg. print-vars 用來打印變量列表,my-dir 可以知道當前的路徑位置
* 以下是 mk 檔之間的依賴調用關係 (時序圖)
> 
* 如果需要定之一款 Android 設備,需要定義以下幾個檔案,^1^ verdorsetup.sh,^2.^ AndroidProducts.mk ,^3^ BoardConfig.mk,^4^ Android.mk
| mk 檔案 | 功能 |
| -------- | -------- |
| **javac.mk** | 選取適合的 java 編譯器 |
| **envsetup.mk** | 環境變量的配置 |
| **AndroidProducts.mk** & **BoardConfig.mk** | 訂製設備 |
| **Android.mk** | 可以從上面關係圖看出它是最後被 main.mk 調用的,它代表了 **零件** 的組成建構 |
### [java.mk](https://android.googlesource.com/platform/build/+/master/core/java.mk) 介紹
* 負責與 java 語言相關的編譯實現,是 `java_library.mk` 的基礎
* java.mk 中定義了多個中間和最中產物
```shell=
## core/java.mk
## 中間產物 LOCAL_INTERMEDIATE_TARGETS
LOCAL_INTERMEDIATE_TARGETS += \
$(full_classes_turbine_jar) \
$(full_classes_compiled_jar) \
$(full_classes_jarjar_jar) \
$(full_classes_jar) \
$(full_classes_combined_jar) \
$(built_dex_intermediate) \
$(built_dex) \
$(full_classes_stubs_jar) \
$(java_source_list_file)
```
* 使用 **全局變量**,最終產生我們需要的檔案
> 
| 全局變量 | 最終產物 | 規則 |
| -------- | -------- | -------- |
| full_classes_compiled_jar | classes-full-debug.jar |  |
| full_classes_processed_jar | classes-processed.jar |  |
| full_classes_jarjar_jar | classes-jarjar.jar |  |
| full_classes_jar | classes.jar | 
|
| full_classes_combined_jar | classes-combined.jar | 
|
| built_dex_intermediate | classes.dex | 
|
| built_dex | classes.dex |  |
| full_classes_stubs_jar | stubs.jar | 
|
| java_sources_deps | Non |  |
| java_source_list_file | java-source-list |  |
| full_classes_turbine_jar | classes-turbine.jar |  |
* 從上面可以看到許多的變數是相互依賴的關係,如下圖
> 
## Android.mk 編寫規則
Android.mk 檔案在 source code 中有大量的出現,引用方式就在 [**main.mk**](https://android.googlesource.com/platform/build/+/master/core/main.mk) 中,Android.mk 對於系統的拓展很有幫中,讓需要的廠商不必了解整個系統的編譯,就可添加系統應用
```shell=
#
# Include all of the makefiles in the system
#
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
# 遍歷 && include
$(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
```
**在編譯整個系統的情況下,系統所找到所有的 ==Android.mk 會先存入 subdir_makefiles== 這個變量中,之後一次性 include 進系統中**
### mk 檔 - 設置參數
* Android mk 檔常見的 **參數如下表**
| 變量名稱 | 說明 | 補充 |
| -------- | -------- | - |
| `LOCAL_PATH` | 用於確定源碼所在的目錄,最好把它放在 `CLEAR_VARS` 變量引用之前,因為 `LOCAL_PATH` 不會被清除,每個 Andriod.mk 只須定義一次 | |
| `CLEAR_VARS` | 它清空很多以 "`LOCAL_`" 開頭的變量(`LOCAL_PATH` 除外),Makefile 都是在一個編譯環境中執行的,因此變量的定義理論上都是全局,每個 Module 前進行清理工作 | |
| `LOCAL_SRC_FILES` | 模塊編譯過程所涉及的原文件,如果是 Java 程序,可以考慮要 `all-subfir-java-files` 來一次性添加目錄(包括子目錄)下所有的 java 文件,因為有 `LOCAL_PATH` 所以這裡只要給出相對路徑 | |
| `LOCAL_CFLAGS` | C 語言編譯時的而外選項 | |
| `LOCAL_MODULE` | 模塊名,需保證在整個編譯系統中唯一的存在,且中間不可以有空格 | |
| `LOCAL_MODULE_TAGS` | 代表當前工程(Android.mk 文件所在的目錄),在哪個模式下被編譯 | 與 **全域參數 `TARGET_BUILD_VARIANT`** 的設置有關 |
| `LOCAL_STATIC_LIBRARIES` | 編譯所需的靜態厙列表 | |
| `BUILD_HOST_EXTCUTEABLE` | 編譯模塊 | |
| `BUILD_SHARED_LIBRARY` | 編譯成 so 文件 | 可執行文件的建立路徑則是 `<Android source>/out/target/product/generic/system/lib/<驅動名>.so` |
| `BUILD_EXTCUTEABLE` | 編譯為可執行模塊 | 可執行文件的建立路徑則是 `<Android source>/out/target/product/generic/system/bin/<驅動名>` |
### Adb 中的 [Android.mk](https://android.googlesource.com/platform/system/core/+/android-4.4_r1/adb/Android.mk)
* Adb 的源碼路徑在 `system/core/adb` 中,以下先來看看該資料夾中的 Android.mk 檔案,注意註解部份
ADB 編譯主要分為了 ^1^ **ADB Host**、^2^ **ADBD** (ADB Daemon)
```shell=
## Android.mk for adb
LOCAL_PATH:= $(call my-dir)
# 清除 LOCAL_PATH 以外的上百個變量 (var)
include $(CLEAR_VARS)
# 特別將這兩個變量提出來,因為在每個系統中,USB 的驅動都不同
USB_SRCS :=
EXTRA_SRCS :=
# 假設目前在 Linux 系統中(其他系統就自己看囉~)
ifeq ($(HOST_OS),linux)
USB_SRCS := usb_linux.c # Linux 專用的 usb 驅動 (usb_linux.c)
EXTRA_SRCS := get_my_path_linux.c
LOCAL_LDLIBS += -lrt -ldl -lpthread
LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
endif
...省略部份
# LOCAL_SRC_FILES 是相當重要的變量,它定義了該模塊 ++ 編譯所涉及的所有原文件 ++
# 可以看到 EXTRA_SRCS、EXTRA_SRCS 也被加入了變量中
LOCAL_SRC_FILES := \
adb.c \
console.c \
transport.c \
transport_local.c \
transport_usb.c \
commandline.c \
adb_client.c \
adb_auth_host.c \
sockets.c \
services.c \
file_sync_client.c \
$(EXTRA_SRCS) \
$(EXTRA_SRCS) \
usb_vendors.c
# 根據狀況拓展 LOCAL_SRC_FILES 變量
ifneq ($(USE_SYSDEPS_WIN32),) # 32 位元系統
LOCAL_SRC_FILES += sysdeps_win32.c
else
LOCAL_SRC_FILES += fdevent.c
endif
# 添加編譯標誌,在編譯過程中會起作用
LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
# 生成模塊的名稱
LOCAL_MODULE := adb
# 編譯過程中需要的厙
LOCAL_STATIC_LIBRARIES := libzipfile libunz libcrypto_static $(EXTRA_STATIC_LIBS)
ifeq ($(USE_SYSDEPS_WIN32),)
LOCAL_STATIC_LIBRARIES += libcutils
# 生成一個 Host 可執行程序
include $(BUILD_HOST_EXECUTABLE)
###########################################################################
# adbd device daemon
# =========================================================
# 第二個模塊編譯開始的標誌
include $(CLEAR_VARS)
# 與上面的第一個模塊部份重複
LOCAL_SRC_FILES := \
adb.c \
backup_service.c \
fdevent.c \
transport.c \
transport_local.c \
transport_usb.c \
adb_auth_client.c \
sockets.c \
services.c \
file_sync_service.c \
jdwp_service.c \
framebuffer_service.c \
remount_service.c \
usb_linux_client.c \
log_service.c
# 添加編譯標誌位
LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
# 模塊名稱
LOCAL_MODULE := adbd
# 編譯時使用到的厙
LOCAL_STATIC_LIBRARIES := liblog libcutils libc libmincrypt
# 生成一個可執行程序
include $(BUILD_EXECUTABLE)
```
:::info
* CLEAR_VARS 定義在 build/core/clear [**vars.mk**](https://android.googlesource.com/platform/ndk/+/froyo/build/core/clear-vars.mk) 中,清除了 LOCAL_PATH 以外的上百個變量
:::
## Jack ToolChain
**從 Android 6.0 版本開始**,Android 編譯系統中有了較大的變化,**可以使用新的 Java 編譯鏈(Java Android Compiler Kit)**
**Jack 的主要任務是取代以前的 javac、proguard、jarjar、dex 等等的諸多工具,以全新的方式將 Java 原文件編譯程 Android 的 Dex 字節碼**
:::info
並請內建 shrinking、obfuscation、repackaging、multidex 並完全開源
:::
### JILL
* Jack 有自己的文件格式,這就不可避免的需要利用一個句將它與傳統的 .jar 文件進行轉換,也就是 JILL(Jack Intermediate Library Linker),如下圖
> 
* 編譯出來的結果並不會有中間的 .jar 文件,會 **直接得到 dex 檔案**
## Appendix & FAQ
:::warning
* Android Kernel 編譯 [**參考**](https://www.owalle.com/2020/05/11/android-emulator/)
* [Android 4 編譯出錯時的解決方案](https://blog.csdn.net/zhangwenhaojf40it/article/details/78110106)、[**Android 4 常見錯誤**](https://jzq84229.github.io/2016/05/18/android-source-make-errors.html)、[**-lGL 錯誤**](https://stackoverflow.com/questions/45556184/how-to-solve-usr-bin-ld-cannot-find-lgl)
> ... Android 4 問題滿多的,要多去 google
>
> 建議使用 [**gcc 4.4、g++ 4.4**](https://blog.csdn.net/zyfzhangyafei/article/details/90410390)
* Root node - droid:Makefile 內沒有書上的內容
:::
###### tags: `Android 系統` `Jack Tool`