Android高版本P/Q/R原始碼編譯指南

IT先森發表於2021-01-04

       Android高版本P/Q/R原始碼編譯指南


Android原始碼編譯系列部落格:

Android.bp你真的瞭解嗎
Android.bp入門指南之Android.mk轉換成Android.bp
Android.bp入門指南之淺析Android.bp語法
Android.bp正確姿勢新增巨集控制編譯指南
Android高版本P/Q/R原始碼編譯指南



引言

  時代在進步,第三套少兒廣播體操!不好意思,搞錯頻道了,重來!時代在進步,Android的版本也是快速的進行著迭代著,從我們以前最常見的Android 4.4一直髮展到了今天的Android 11版本(即Android K到Android R),Android版本的快速迭代對於消費者來說是一件普天同慶的大好事情,但是對於我們開發者來說各種適配各種改造有時候吃翔的心情都有了。而對於Android版本的適配和各種改造的第一步就是從編譯Android原始碼開始,可是不幸的是隨著Android版本的迭代連編譯Android原始碼的相關流程都發生了翻天覆地的變化,正所謂工欲利其事必先利器,所以我們今天的這篇部落格將帶領讀者一起來捯飭捯飭Android各個版本的原始碼編譯發展和編譯具體操作步驟!

這裡我們需要注意一下Android各個版本的對應關係如下:

  • Android 5.x (Lollipop)簡稱Android L版本
  • Android 6.0 (MarshMallow) 簡稱Android M版本
  • Android 7.x (Nougat)簡稱Android N版本
  • Android 8.x (Oreo)簡稱Android O版本
  • Android 9.0 (Pie)簡稱Android P版本
  • Android 10.0 (Q)簡稱Android Q版本
  • Android 11.0 (R)簡稱Android R版本

並且這裡還有一點需要特別注意,本文演示的Android R版本是以高通平臺為基準進行的。




一.Android編譯環境的構建以及常見命令

  俗話說天時地利人和缺一不可,而這其中的地利翻譯過來說的就是環境因素了,人的成長離不開環境因素,而我們的Android編譯也離不開編譯環境的構建!雖然我們本篇部落格的主題是Android原始碼編譯指南,但是我們還是有必要抽出一個章節來簡單說明下Android編譯環境的構建和初始化過程,以及初始化完畢後常見的命令。


1.1 Android編譯環境的構建

本章節重點偏向的是Android編譯環境的構建,而不是編譯環境構建的原理分析(如果是原理分析,那內容就多了)。

雖然Android的版本一直在迭代著,但是Android編譯環境的構建步驟還是比較良心的依然沒有多大的變化(注意這裡的措辭,只是步驟),對於有過Android原始碼開發經驗的讀者來說是再為熟悉不過的了,通常是如下二部曲:

$ source build/envsetup.sh
$ lunch aosp-eng

雖然各位對上述的命令應該已經爛熟於心了,但是這裡我還是簡單說明一下:

  • 第一行命令”source build/envsetup.sh”引入了build/envsetup.sh 指令碼。該指令碼的作用是初始化編譯環境,並引入一些輔助的Shell函式,這其中就包括第二步使用 lunch 函式

  • 第二行命令”lunch aosp-eng”是呼叫 lunch 函式,並指定引數為”aosp-eng”。lunch 函式的引數用來指定此次編譯的目標裝置以及編譯型別。在這裡,這兩個值分別是”aosp”和”eng”。”aosp”是 Android 原始碼中已經定義好的一種產品,是為模擬器而設定的。而編譯型別會影響最終系統中包含的模組。如果在呼叫lunch函式的時候沒有指定引數,那麼該函式將輸出列表以供選擇,列表內容不同Android版本,不同廠家的基線原始碼會有所不同,如下:

在這裡插入圖片描述

這裡補充一點對Android的原始碼編譯型別簡單說明一下,它可以分為如下三種功能,每種型別的特點如下:

在這裡插入圖片描述


1.2 Android編譯各種常見命令

在編譯環境初始化完成後,我們就可以使用各種各種編譯環境提供的指令和make編譯命令族來開啟Android的構建之旅了,這裡我簡單的總結了下,我們在Android編譯中可能會用到的編譯環境提供的指令和make編譯命令族,如下:

1.2.1 常見的Android命令指令

指令說明
  croot   切到Android原始碼樹的根目錄(當你深入Android原始碼樹的子目錄,想回到根目錄的時候此命令就非常實用了)
m相當於在原始碼樹的根目錄執行make,並且該命令不一定要在根目錄下執行
mm編譯當前目錄路徑下的所有模組(包括include進來的,但是不包括存在依賴關係模組)
mma編譯當前目錄路徑下的所有模組(包括include進來的,且包括存在依賴關係模組)
mmm[module_path]編譯指定目錄路徑下的所有模組(包括include進來的,但是不包括存在存在依賴關係模組)
mmma[module_path]編譯指定目錄路徑下的所有模組(包括include進來的,包括存在存在依賴關係模組)
cgrep對C/C++檔案執行grep(即grep的時候只搜尋C/C++檔案型別,注意這裡也包括.h檔案型別)
jgrep對Java檔案執行grep(即grep的時候只搜尋Java檔案型別)
resgrep在所有res/.xml檔案上執行 grep即grep的時候只搜尋res/.xml檔案型別)
printconfig顯示當前Android編譯的相關配置資訊
add_lunch_combo在lunch命令的的選單中新增一個條目

這裡我們對上述表格中的不包括存在依賴關係模組::
1.依賴關係模組這個要怎麼說呢,這裡我們舉個例子!譬如模組A的編譯需要依賴模組B,此時的B是一個so庫。
2.假如我們通過mm或者mmm編譯模組A的時候,此時B模組還沒有編譯那麼此時就會報錯
3.假如我們使用的是mma或者mmma編譯模組A,假如依賴的模組B還沒有編譯,那麼會先將模組B編譯OK,然後編譯模組A(當然這裡只是舉栗子,可能A還依賴C,D同理也會先編譯)

1.2.2 make編譯命令族

Android的Build編譯系統處理常見的make單命令之外,還提供了其它的一系列make命令族,這裡我們簡單過下:

指令說明
make update-api更新API檔案,在framework API改動之後,需要首先執行該命令來更新API,公開的API記錄在frameworks/base/api目錄下
makeAndroid預設系統編譯指令,會編譯出整個系統的所有映象(其實質最終執行的是make droid)
make droid同上
make sdk編譯出Android的SDK開發套件
make clean-sdk清理SDK的編譯產物
make dist執行整個編譯,並將 MAKECMDGOALS變數定義的輸出檔案拷貝到 /out/dist目錄下,
這個命令在實際中用的比較少
make all編譯所有內容,不管當前產品的定義中是否會包含,官方解釋如下:
builds everything make droid does,plus everything whose LOCAL_MODULE_TAGS do not include the “droid” tag. The build server runs this to make sure that everything that is in the tree and has an Android.mk builds.
make help幫助資訊命令,顯示當前Android版本主要支援的make命令
make snod從已經編譯出的包快速構建系統映象(譬如你重新單獨編譯了某個模組,然後想快速進行打包到system.img,可以使用此命令加快速度)
make clean-$(LOCAL_MODULE)Let you selectively clean one target. For example, you can type make clean-libutils and t will delete libutils.so and all of the intermediate files.
即清理掉一個指定模組的編譯結果和中間產物
make clean-$(LOCAL_PACKAGE_NAME)Let you selectively clean one target. For example, you can type
make clean-Home and it will clean just the Home app…
即清理掉一個指定模組的編譯結果和中間產物
make cleandeletes all of the output and intermediate files for this configuration. This is the same as rm -rf out/<configuration>/
通常刪除的是整個Android原始碼工程的out/*目錄
make clobberdeletes all of the output and intermediate files for all configurations. This is the same as rm -rf out/.
這個命令在實際中,應用得比較少
make datacleandeletes contents of the data directory inside the current combo
directory. This is especially useful on the simulator and emulator, where the persistent data remains present between builds.
這個命令在實際中應用得也比價少
make installclean當我們在執行切換編譯目標時可以執行make installclean,用以清除之前編譯生成的檔案,但是又不會將整個out目錄清空,這樣可以加快編譯目標的構建速度
make LOCAL_MODULE編譯一個指定的模組,LOCAL_MODULE 為模組的名稱,這種編譯方法通常運用在整個Android工程沒有構建,但是想快速編譯一個模組時可以使用,可以加快單個模組構建速度
make targetswill print a list of all of the LOCAL_MODULE names you can make.
make libandroid_runtime編譯所有JNI framework內容。
make framework編譯所有Javaframework內容(做Android framework開發的小夥們對這條命令應該是再熟悉不過的了)。
make services編譯系統服務和相關內容
make bootimage編譯生成boot.img
make recoveryimage編譯生成recovey.img
make cacheimage編譯生成cache.img
make systemimage編譯生成system.img
make vendorimage編譯生成vendor.img
make superimage編譯生成superi.img

對上述的make命令有幾點需要注意:
1.可能在不同的Android版本有不同表現,且有的可能已經不支援了
2.讀者最好對於每個make編譯命令,自行使用一番,然後慢慢品嚐




二.Android編譯的發展史簡介

  有過一定Android開發經驗的讀者應該知道Android最初是用Android.mk配置來編譯原始碼的(這裡的Android.mk本質上有點類似Makefile檔案)。但是隨著Android版本的迭代,原始碼工程檔案越來越大,包含的模組越來越多,而以Android.mk組織的專案編譯花費的時間越來越多。面對這個嚴峻的問題,Android的媽咪谷歌終於在在Android7.0開始引入了ninja編譯系統。相對於make來說ninja在大的專案管理中速度和並行方面有突出的優勢,因此Google採用了ninja來取代之前使用的make。由於Android.mk的數量巨大且複雜,不可能把所有的Android.mk改寫成ninja的構建規則,因此Google搞了個kati工具,用於將Androd.mk轉換成ninja的構建規則檔案build.ninja,再使用ninja來進行構建工作。

Android編譯的發展依然沒有停止進化,果不其然Android8.0開始,Google引入了Android.bp檔案來替代之前的Android.mk檔案,Android.bp只是純粹的配置檔案,不包括分支、迴圈等流程控制,本質上就是一個json配置檔案。同時還引入Soong這個工具,用於將Android.bp轉換為ninja的構建規則檔案build.ninja,再使用ninja來進行構建工作。但之前的模組全部是用Android.mk來定義的,google不可能一下子把所有模組都修改成Android.bp,只能逐步替換。無論是Android.mk還是Android.bp最後都是轉化成ninja的構建規則,再進行編譯的。

如果你對上述的概述,還是覺得太麻煩了,這裡我們整體來概括一下Android build系統隨著Android版本相應的發展演變過程:

  • Android 7.0引入ninja和kati
  • Android 8.0使用Android.bp來替換Android.mk,引入Soong
  • Android 9.0強制使用Android.bp

2.1 Soong、Blueprint、Kati、Ninja關係

前面一頓咔咔,我們簡單介紹了Android編譯系統的範展示,其中突然一下子冒出了許多的概念,這裡我們先暫且不對其中涉及的概念講述,我們先說說Soong、Blueprint、Kati、Ninja之間的關係,如下:

在這裡插入圖片描述

上圖是整體的關係圖,同時在Android原始碼工程構建過程中的轉換關係如下:

在這裡插入圖片描述

如果對上述的關係還是沒有捯飭清楚的,我們再來說說,說說:

  • 首先通過Kati將Android.mk轉換成ninja格式的檔案
  • 通過androidmk將將Android.mk轉換成Android.bp,但是隻針對沒有分支、迴圈等流程控制的Android.mk才有效,如果對於有控制流的就必須手動了具體可以想見部落格Android.bp正確姿勢新增巨集控制編譯指南
  • 通過Blueprint+ Soong將Android.bp轉換成ninja格式的檔案

不容易啊,這裡我們對涉及到Ninja, kati, Soong, bp關係搞清楚了(各種三角戀)!那麼關於它們的概念,接下來我們也得簡單介紹介紹,安排上才行!


2.2 Kati簡介

Kati是專為Android開發的一個基於Golang和C++的工具,主要功能是把Android中的Android.mk檔案轉換成 Ninja檔案。程式碼路徑是build/kati,編譯後的產物是ckati。

Kati程式碼是開源的,可以把它clone下來,如果感興趣可以檢視下其實現原理

這裡我們構建一個通過Android.mk配置的LOCAL_MODULE模組,然後通過top命令就可以檢視在編譯的過程中執行了ckati的命令。
在這裡插入圖片描述


2.3 Ninja簡介

ninja是一個編譯框架,會根據相應的ninja格式的配置檔案進行編譯,但是ninja檔案一般不會手動修改,而是通過將Android.bp檔案轉換成ninja格檔案來編譯。


2.4 Android.bp簡介

Android.bp的出現就是為了替換Android.mk檔案。而bp跟mk檔案不同,它是純粹的配置,沒有分支、迴圈等流程控制,不能做算數邏輯運算。如果需要控制邏輯,那麼只能通過Go語言編寫。Android的媽咪谷歌為了讓開發者能更加的快速掌握Android.bp特意提供了androidmk命令(關於它的詳細介紹可以參見部落格Android.bp入門指南之Android.mk轉換成Android.bp,這裡就不過多的戲說了)用於Android.mk轉換成Android.bp使用,如下轉換命令:

$ androidmk Android.mk > Android.bp

2.5 Blueprint和Soong構建編譯系統

2,5.1 Soong簡介

Soong類似於之前的Makefile編譯系統的核心,負責提供Android.bp語義解析,並將之轉換成Ninja檔案。Soong還會編譯生成一個androidmk命令,用於將Android.mk檔案轉換為Android.bp檔案,不過這個轉換功能僅限於沒有分支、迴圈等流程控制的Android.mk才有效。

2.5.2 Blueprint簡介

Blueprint是生成、解析Android.bp的工具,是Soong的一部分。Soong負責Android編譯而設計的工具,而Blueprint只是解析檔案格式,Soong解析內容的具體含義。Blueprint和Soong都是由Golang寫的專案,從Android 7.0,prebuilts/go/目錄下新增Golang所需的執行環境,在編譯時使用。並且因為Soong和Blueprint是Google谷歌為Android.bp特別定製的工具,所以不需要要摘出來單獨來操作。




三.高版本P/Q/R原始碼編譯

  通過前面的章節我們瞭解Android編譯環境的基本構建和編譯的發展史,那麼本章節將重點分析Android O之後高階版本的編譯的不同之處。並且本文的部落格前年也有說到是以高通版本的Android為基線的。所以在開始本章節的部落格前,有兩個知識點需要提前介紹下,一個是Android Q以及之後的動態分割槽,以及qssi的概念!


3.1 Android動態分割槽

動態分割槽是Android的使用者空間分割槽系統。使用此分割槽系統,您可以在無線下載(OTA)更新期間建立、銷燬分割槽或者調整分割槽大小。藉助動態分割槽,供應商無需擔心各個分割槽(例如system、vendor和product)的大小。取而代之的是,裝置分配一個super分割槽,其中的子分割槽可動態地調整大小。單個分割槽映像不再需要為將來的OTA預留空間。相反,super中剩餘的可用空間還可用於所有動態分割槽(關於動態分割槽詳見谷歌官方Android實現動態分割槽)。

在這裡插入圖片描述



3.2 什麼是QSSI

QSSI 是 Qualcomm Single System Image 的縮寫,並且高通平臺從Android Q開始支援。並且其編譯也和Android原生編譯有差別,其差別如下:
在這裡插入圖片描述



3.3 具有QSSI特性Android關鍵的整體編譯流程

通過前面看到QSSI特性的韌體編譯流程也和通用版本的有一定的區別,這裡的編譯分為兩種模式,第一種Android的標準編譯模式,另外一種就是高通提供的編譯指令碼。

這裡需要注意的的是通用版本的Android還是可以直接通過make相關的分割槽進行直接編譯的,譬如make superimage或者直接執行make編譯

3.3.1 通過Android內建make命令編譯

source build/envsetup.sh

  • 編譯 system.img
    lunch qssi-userdebug
    make target-files-package
  • 編譯除system.img外的其他img
    lunch xx-userdebug
    make target-files-package

3.2.2 高通提供的build.sh指令碼進行編譯

  • 編譯所有img,包括system和其它img
source build/envsetup.sh
lunch XX-userdebug
./build.sh dist -j32
  • 編譯system.img,產物在qssi目錄下
source build/envsetup.sh
lunch xx-userdebug
./build.sh dist qssi_only -j32
  • 編譯super.img
source build/envsetup.sh
lunch xx-userdebug
./build.sh dist merge_only -j32
  • 編譯其它img,例如vendorimage,如果不指定會編譯其它所有img,產物在XX目錄下
source build/envsetup.sh
lunch xx-userdebug
./build.sh vendorimage dist target_only -j32


3.4 非QSSI特性的整體編譯流程

非QSSI特性的編譯流程,依然和以前的版本Android編譯變化不大,通常是如下的步驟:

source build/envsetup.sh 
lunch xx-userdebug
make

3.5 動態分割槽刷機的方法

Android Q版本以及以上將system和vendor分割槽合併為super分割槽,無法通過adb reboot bootloader模式單獨刷動態分割槽裡面的img,例如system,vendor,product,odm,只能刷super.img和其他的,但是fastboot模式下可以單獨刷動態分割槽裡面的img,其方法如下:

#推薦進入fastboot模式刷機:
adb reboot fastboot
fastboot getvar is-userspace
is-userspace: yes
Finished. Total time: 0.002s
fastboot flash vendor vendor.img
fastboot flash system system.img
fastboot flash vbmeta vbmeta.img
fastboot flash vbmeta_system vbmeta_system.img
#fastbootd是使用者空間的程式碼,因為動態的邏輯分割槽只能在應用空間識別

1.如果是在linux下fastboot刷機出現許可權問題,需要將fastboot的所有者屬性改成root
sudo chown root:root fastboot
sudo chmod +s fastboot
2.如果是在windows環境下使用fastboot,很大概率可能不識別fastboot,此時推薦下載360的手機助手藉助它安裝對應的驅動,這樣就能進行相關的識別了,此處是個人經驗


3.6.Framework編譯

現在Android R之上的Framework的編譯已經和之前有所不同,具體參見下面解釋:

  • Android R之前單獨編譯framework和service命令為:
make -j8 framework
make -j8 services
  • Android R之後的命令為:
make -j8 framework-minus-apex
make -j8 services




四.Android為啥要引入動態分割槽

  在前面我們簡單說了下動態分割槽的概念,即在Android Q以及以後得編譯包中,我們找不到了對應的system,vendor等img檔案,但是多了一個super.img,system,vendor,product,ODM合併為super分割槽,這個就是動態分割槽了。簡單來說就是為了在ota的時候能夠靈活建立分割槽和修改分割槽大小,將system,vendor,odm,product合併成super分割槽,並在super分割槽上預留出一定量的free space,這樣就可以動態調整這些分割槽的大小,解決了ota的時候分割槽不足,以及調整分割槽的風險.。


在這裡插入圖片描述
當OTA升級之後,需要重新調整分割槽大小:

在這裡插入圖片描述




寫在最後

  好了今天的部落格Android高版本P/Q/R原始碼編譯指南就到這裡了,由於這是一篇實戰型別的部落格所以也沒有多少總結的了,跟著幹就行了。總之,青山不改綠水長流先到這裡了。如果本部落格對你有所幫助,麻煩關注或者點個贊,如果覺得很爛也可以踩一腳!謝謝各位了!!好了,青山不改綠水長流,各位江湖見!當然各位讀者的點贊和關注是我寫作路上前進的最大動力了,如果有啥不對或者不爽的也可以踩一踩也無妨!

最後附上參考文件路徑AndroidP/Q/R編譯系統,這裡我對做了簡化,並且加上了自己實際開發中的一些理解,主要是為了方便大家快速入手Android高版本編譯。

相關文章