kyle shanks
  • NEW!
    NEW!  Connect Ideas Across Notes
    Save time and share insights. With Paragraph Citation, you can quote others’ work with source info built in. If someone cites your note, you’ll see a card showing where it’s used—bringing notes closer together.
    Got it
      • Create new note
      • Create a note from template
        • Sharing URL Link copied
        • /edit
        • View mode
          • Edit mode
          • View mode
          • Book mode
          • Slide mode
          Edit mode View mode Book mode Slide mode
        • Customize slides
        • Note Permission
        • Read
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Write
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invite by email
        Invitee

        This note has no invitees

      • Publish Note

        Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

        Your note will be visible on your profile and discoverable by anyone.
        Your note is now live.
        This note is visible on your profile and discoverable online.
        Everyone on the web can find and read all notes of this public team.

        Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Explore these features while you wait
        Complete general settings
        Bookmark and like published notes
        Write a few more notes
        Complete general settings
        Write a few more notes
        See published notes
        Unpublish note
        Please check the box to agree to the Community Guidelines.
        View profile
      • Commenting
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
        • Everyone
      • Suggest edit
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
      • Emoji Reply
      • Enable
      • Versions and GitHub Sync
      • Note settings
      • Note Insights New
      • Engagement control
      • Make a copy
      • Transfer ownership
      • Delete this note
      • Save as template
      • Insert from template
      • Import from
        • Dropbox
        • Google Drive
        • Gist
        • Clipboard
      • Export to
        • Dropbox
        • Google Drive
        • Gist
      • Download
        • Markdown
        • HTML
        • Raw HTML
    Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
    Create Create new note Create a note from template
    Menu
    Options
    Engagement control Make a copy Transfer ownership Delete this note
    Import from
    Dropbox Google Drive Gist Clipboard
    Export to
    Dropbox Google Drive Gist
    Download
    Markdown HTML Raw HTML
    Back
    Sharing URL Link copied
    /edit
    View mode
    • Edit mode
    • View mode
    • Book mode
    • Slide mode
    Edit mode View mode Book mode Slide mode
    Customize slides
    Note Permission
    Read
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Write
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: 'Android 下載源碼、編譯系統' disqus: kyleAlien --- Android 下載源碼、編譯系統 === ## OverView of Content 如有引用該文章請標明引用,謝謝 :smile_cat: 以下是在 Ubuntu20 LTS 環境下進行 [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 ``` > ![image](https://hackmd.io/_uploads/ry_ZJkkUp.png) ### 下載 [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` 目錄 > > ![](https://i.imgur.com/XLyXYgY.png) :::success * 可以透過查看 `.repo/manifests/default.xml` 檔案來查看當前的設置 ```shell= cat .repo/manifests/default.xml | head -n 10 ``` > ![](https://hackmd.io/_uploads/H1XdbUFG6.png) ::: 3. `repo sync`:sync 關鍵字就是開始下載;如果下載中途發生異常,仍可以使用 `repo sync` 再次同步繼續下載 ```shell= repo sync ## 如果想加速則可以指定 -j (線程使用數量)、-c、-q (安靜模式) repo sync -c -j12 ## 使用 12 核下載 ## FIX SYNC ERROR CMD # find .repo -name "*.lock" -delete # repo sync -c -j1 --force-sync --no-tags ``` 下載完後接近 192G,資料相當龐大~ 請分配好空間 (Build 後會更耗費空間) > ![](https://i.imgur.com/DzlgpcS.png) ### 編譯 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 ``` > ![](https://i.imgur.com/PowDXTo.png) 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` 版本 ``` > ![reference link](https://i.imgur.com/s0gJjro.png) 3. `make` 編譯:可以按照自己電腦的 CPU 核心數量來編譯 ```shell= # 目前指定 12 核心 (全力編譯) # make -j$(nproc) make -j12 ``` > ![](https://i.imgur.com/jnWmxwD.png) :::success * **`sdk_phone` 的選項** Android 12 的 lunch 沒有輸出 `sdk_phone` 的選項,但這部分仍可以在 `build/target/product` 資料夾那找到,所以仍可以指定 > ![](https://i.imgur.com/jTkkpH9.png) ```shell= ## 隱藏選項 lunch sdk_phone_x86 ## repo sync -c -j12 make -j$(nproc --all) ``` > ![](https://i.imgur.com/kbl30S1.png) ::: ### 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++**,並包含其依賴 | > ![](https://hackmd.io/_uploads/Sk4_X8HGT.png) :::info * 在執行完 `source ./build/envsetup` 命令後,就可以使用 `help` 指令查看還有哪些命令可用 ::: * 範例:**單編譯 Setting 模塊後的輸出** > ![](https://i.imgur.com/peqJNpi.png) 使用編譯好的 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` 核心 > ![image](https://hackmd.io/_uploads/SyILKez86.png) ::: 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` 目錄 > > ![](https://i.imgur.com/XLyXYgY.png) 3. `repo sync`:sync 關鍵字就是開始下載 ```shell= repo sync ## 如果想加速則可以指定 -j (線程使用數量)、-c、-q (安靜模式) repo sync -c -j12 ## 使用 12 核下載 ``` > ![](https://i.imgur.com/gHKSU4m.png) ### 編譯 Android Kernel * 首先 apt 安裝 tool 編譯 Android kernel 時必須的一些工具 ```shell= ## 多安裝 openssl tool sudo apt install libssl-dev ``` * 進到 Android Kernel 下載好的資料夾根目錄中,並執行以下命令 ```shell= ## 進入資料夾 cd ~/android_kernel ## 編譯命令 ./build/build.sh ``` 最終編譯結果 > ![](https://i.imgur.com/yrFtxX3.png) ### 下載並編譯 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 版本就沒有這個設定檔 > > ![image](https://hackmd.io/_uploads/HJZlLAfUp.png) * 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** > ![](https://i.imgur.com/FTJxS8K.png) * **Custome Kernel Version** > ![](https://i.imgur.com/ynsEaQ4.png) ### 模擬器運行 * 如果你已經手動編譯完成,就不需要指定,直接在 Android source root 根資料夾下使用以下指令即可 > 如果你尚未編譯完,是無法直接使用 `emulator` 命令的,如果已經完成則可以執行以下指令來載入環境變數 ```shell= source ./build/envsetup.sh lunch 39 emulator ``` > ![](https://i.imgur.com/SAQWY2x.png) :::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 ``` > ![](https://i.imgur.com/N77ibSJ.png) ::: :::danger * **缺失 `userdata-qemu.img`**: 這是缺失設備 SDK 導致 ! 切換到 lunch 編輯指定 sdk 版本,並重新指定下載、編譯 > ![](https://i.imgur.com/zUMlOck.png) ```shell= ## 切換到 lunch 66 lunch 66 repo sync -c -j12 make -j12 ``` > ![](https://i.imgur.com/FybRAzx.png) ::: ## 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 的依賴關係** (箭頭表示了依賴的對象) > ![](https://i.imgur.com/AjOoEAJ.png) 由這張圖可以看到,由上而下的分析方式,Android 編譯系統也可以使用相同的方式分析 ### Android 編譯系統抽象模型 * 因為是基於 Makefile 實現,整個編譯系統的核心仍是如何建構出有效的依賴樹 > ![](https://i.imgur.com/gP03NRw.png) 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'`;不代表編譯失敗 > ![image](https://hackmd.io/_uploads/SkKDPHQ8a.png) ::: * 接下來看看 [**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** 進行分析 **--根依賴樹--** > ![](https://i.imgur.com/P53hwqo.png) ### 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 檔之間的依賴調用關係 (時序圖) > ![](https://i.imgur.com/BdRa83V.png) * 如果需要定之一款 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) ``` * 使用 **全局變量**,最終產生我們需要的檔案 > ![](https://i.imgur.com/DbJW52E.png) | 全局變量 | 最終產物 | 規則 | | -------- | -------- | -------- | | full_classes_compiled_jar | classes-full-debug.jar | ![](https://i.imgur.com/912rB4b.png) | | full_classes_processed_jar | classes-processed.jar | ![](https://i.imgur.com/lvtBZf0.png) | | full_classes_jarjar_jar | classes-jarjar.jar | ![](https://i.imgur.com/oP9XErA.png) | | full_classes_jar | classes.jar | ![](https://i.imgur.com/qphoz9a.png) | | full_classes_combined_jar | classes-combined.jar | ![](https://i.imgur.com/jUvbD99.png) | | built_dex_intermediate | classes.dex | ![](https://i.imgur.com/BaWEZcT.png) | | built_dex | classes.dex | ![](https://i.imgur.com/wGnaI7O.png) | | full_classes_stubs_jar | stubs.jar | ![](https://i.imgur.com/rplQt9w.png) | | java_sources_deps | Non | ![](https://i.imgur.com/bQEprL0.png) | | java_source_list_file | java-source-list | ![](https://i.imgur.com/EvSSJtI.png) | | full_classes_turbine_jar | classes-turbine.jar | ![](https://i.imgur.com/TJs8yOq.png) | * 從上面可以看到許多的變數是相互依賴的關係,如下圖 > ![](https://i.imgur.com/uWSKY4c.png) ## 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),如下圖 > ![](https://i.imgur.com/e4qCzVQ.png) * 編譯出來的結果並不會有中間的 .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`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully