android系统源码的环境下用make来编译,Android源码编译系统入门
做過 Android 平臺開發的朋友對make,mm或make clean命令應該很熟悉,但也許大家只是熟知這些命令的作用卻不知道這些命令底下有些什么原理?那么今天我就帶著大家推開Android編譯系統的大門,探索一下這片未知的恐怖之森(問啥要用恐怖之森后面大家就知道了)。
Makefile入門
在講解Android編譯系統之前首先來了解一下什么是Makefile:
簡單的說,Makefile提供了一種機制,讓使用者可以有效的組織工作。
注意這里用的是“工作”而非“編譯”,這是因為Makefile并不是用來完成編譯工作的,它只是一種規則的執行者,而使用者用它來執行什么規則沒有任何限制。如既可以用它來編譯系統,也能用它備份文檔或只是打印log。
既然它是用來執行規則的,那么我們就來看看Makefile的規則。
TARGET:PREREQUISITES
COMMANDS
注意:COMMANDS前面必須有一個TAB制表符
在 Makefile 的規則中TARGET是需要生成的目標文件,PREREQUISITES是目標的先決條件,也是另一個規則中的target。COMMANDS是生成目標文件的命令,當 prerequisites 中的任何一個文件比 target 文件要新的時候就會觸發 commands。commands 的具體內容取決于使用者的需求,如調用 gcc 編譯器。
下面我們用一個簡單的例子說明一下Makefile規則的用法。
涉及到的文件如下:
文件名
描述
mian.c
主函數所在文件
test.h
提供一個測試函數getNumber的聲明
test.c
提供getNumber的函數實現,用于返回一個值
Makefile
我們的主角,用于編譯整個例子
文件內容如下:
(1)test.h對getNumber進行聲明。
int getNumber();
(2)test.c實現getNumber函數
#include "test.h"
int getNumber()
{
return 2333;
}
(3)main.c打印getNumber的返回值
#include
#include "test.h"
int main()
{
printf("Hello,getNumber=%d\n",getNumber());
return 0;
}
(4)Makefile文件,用于組織這個小例子的編譯工作
MakefileTest : main.o test.o
gcc -o MakefileTest main.o test.o
main.o : main.c
gcc -c main.c
test.o : test.c
gcc -c test.c
對上面這段代碼簡單解釋一下,MakefileTest為TAEGET即目標產物,main.o和test.o是MakefileTest的先決條件。gcc -o MakefileTest main.o test.o則是MakefileTest對應的COMMANDS,這條命令使用gcc命令將main.o和test.o編譯成MakefileTest可執行文件。
最后通過使用make命令就會在當前目錄生成一個MakefileTest可執行文件,運行結果如下:
這里寫圖片描述
上面這個是一個非常簡單的Makefile的示例,但“麻雀雖小,五臟俱全”,它清晰的展示了一個Makefile的編寫過程。另外Make工具本身是非常強大的,它有很多隱含的規則來幫助開發者快速搭建復雜的編譯體系。如利用它的自動推導功能可以簡化對象間的依賴關系,還可以加入變量來減少重復輸入。
上面的Makefile還可以寫成這樣:
OBJECT = main.o test.o
MakefileTest : $(OBJECT)
gcc -o MakefileTest $(OBJECT)
關于Makefile的更多用法可以查看how to write makefile。希望大家多了解一下makefile的用法,這有助于理解Android的編譯系統。
Android編譯系統入門
像我這樣從事過Android平臺開發的開發人員對make,mm等命令應該很熟悉,同樣也知道Android.mk文件,用它結合mm命令來編譯單個的Android模塊。如果這就是你對Android編譯系統的全部了解,那只能說你是一個普通的Android平臺開發人員。想要在Android平臺開發界混的話,學習和掌握Android編譯系統的原理將是必不可少的。前面說道Android編譯系統是一個恐怖之森,應為它真好符合恐怖之森的兩個特點,讓人望而卻步和容易迷失方向。這么說一點也不過分,在學習Android編譯系統的時候,如果缺少很明確的指引很容易迷失方向,或者讓你根本就不敢去試著闖一闖。
makefile依賴樹的概念
不難發現在makefile中的target的依賴關系實際上可以組成一棵樹,我將其稱為makefile依賴樹,仍然以上一個MakefileTest為例,給出它的makefile依賴樹:
這里寫圖片描述
只要照個這個樹形結構順藤摸瓜,那么分析Android編譯系統也就變得有目的性了。
恐怖之森里的樹
有了上面的知識做鋪墊,我們也就可以大膽的往恐怖之森闖了。
這里寫圖片描述
根節點
既然是樹,肯定有它的根節點,我們就來找一找Android編譯系統的根節點。
如果make命令沒有指定文件的話默認會在當前目錄尋找Makefile這個文件,所以先看看Android源碼根目錄的那個Makefile文件:
### DO NOT EDIT THIS FILE ###
include build/core/main.mk
### DO NOT EDIT THIS FILE ###
原來這里只是指路牌,真正的文件是build/core/main.mk。
在 Makefile 使用 include 關鍵字可以把別的 Makefile 包含進來,這很像 C 語言的
\#include,被包含的文件會原模原樣的放在當前文件的包含位置。
打開main.mk發現它有上千行代碼,并且在其中又包含了很多其他makefile腳本,所以整個文件的內部結構讓人感覺雜亂無章,無法入手。這時候我們就不能一行行的去讀這個文件,這樣很可能會事倍功半。我們需要找出它其中依賴樹的根節點,以此為突破口。但往往大型的工程中不止一棵依賴樹,這時候如果沒有指定依賴樹的話make命令會以從上至下第一個target作為默認依賴樹的根節點。其實大家非常熟悉的make clean中的clean就是這些依賴樹中的一棵。
.PHONY: clean
clean:
@rm -rf $(OUT_DIR)/*
@echo "Entire build directory removed."
clean是一個偽目標,偽目標一般沒有目標文件。且使用.PHONY顯示聲明。
從上面clean的COMMANDS知道它的作用是刪除\$(OUT_DIR)下的所有目錄和文件,而\$(OUT_DIR)就是out目錄。
OK,那么我們就跟著上面的思路找尋那棵默認依賴樹,對照main.mk代碼很快就發現了它的默認依賴樹根節點:
# This is the default target. It must be the first declared target.
.PHONY: droid
DEFAULT_GOAL := droid
$(DEFAULT_GOAL):
從注釋中可以看出來droid就是我們找的依賴樹的根節點,不過這里只是定義了一下,沒有給出真正的規則。根據關鍵字搜索的話會發現main.mk中有幾處對droid規則的定義:
ifneq ($(TARGET_BUILD_APPS),)
# If this build is just for apps, only build apps and not the full system by default.
...
.PHONY: apps_only
apps_only: $(unbundled_build_modules)
droid: apps_only
...
else # TARGET_BUILD_APPS
...
# Building a full system-- the default is to build droidcore
droid: droidcore dist_files
上面會根據TARGET_BUILD_APPS變量的值是否為空來走不同的分支,如果不為空,則droid的先決條件是apps_only,如果為空droid的先決條件是droidcore和dist_files,我們使用默認的make命令編譯android系統的話這里的TARGET_BUILD_APPS的值為空,所以走下面這個分支。TARGET_BUILD_APPS何時不為空,感興趣的朋友可以自行分析一下。
main.mk總覽
在分析droidcore和dist_files兩個先決條件之前先來看一下main.mk的一個文件結構,它除了構建droid等依賴樹外,有一大半內容是在做一下這些事情。
對編譯環境的檢測
比如java環境是否符合要求,當前是linux系統還是mac系統。如果這些檢測中有任何一項不符合要求,則會終止編譯。
進行一些必要的前期處理
比如整個項目工程是否要進行清理操作,部分工具的安裝等。
引用其他Makefile文件
比如引用config.mk,cleanbuild.mk等。
設置全局變量
各種函數的實現
Android編譯系統中定義了很多實用的函數,它們提供了整個編譯系統的統一解決方案。比如my-dir這個在Android.mk中出鏡率最高的函數,就是用來獲得當前的路徑。
下表是對Android編譯系統中涉及的主要Makefile文件的解釋,可供參考。
Name
Description
main.mk
整個編譯系統的主導文件
config.mk
產品配置的主導文件
base_rules.mk
編譯系統需要遵循的基礎規則定義
build_id.mk
版本id號的定義
cleanbuild.mk
clean操作的定義
clear_vars.mk
LOCAL開頭的相關系統變量
definitions.mk
提供了大量實用的函數定義
envsetup.mk
配置編譯時的環境變量
executable.mk
負責BUILD_EXECUTABLE的具體實現
host_executable.mk
負責BUILD_HOST_EXECUTABLE的具體實現
host_static_library.mk
負責BUILD_HOST_STATIC_LIBRARY的具體實現,其他類型的BUILD_XX這里不再贅述
product_config.mk
產品級別的配置,屬于config的一部分
version_defaults.mk
負責生成版本信息,如版本號BUILD_NUMBER := eng.$(USER).$(shell date +%Y%m%d.%H%M%S)
droidcore節點
droid規則的定義如下:
# Build files and then package it into the rom formats
.PHONY: droidcore
droidcore: files \
systemimage \
$(INSTALLED_BOOTIMAGE_TARGET) \
$(INSTALLED_RECOVERYIMAGE_TARGET) \
$(INSTALLED_USERDATAIMAGE_TARGET) \
$(INSTALLED_CACHEIMAGE_TARGET) \
$(INSTALLED_VENDORIMAGE_TARGET) \
$(INSTALLED_FILES_FILE)
可以看出來droidcore有如下幾個先決條件,
Prerequisite
Description
files
代表其所依賴的先決條件的集合,沒有實際意義
systemimage
將生成system.img
INSTALLED_BOOTIMAGE_TARGET
將生成boot.img
INSTALLED_RECOVERYIMAGE_TARGET
將生成recovery.img
INSTALLED_USERDATAIMAGE_TARGET
將生成userdata.img
INSTALLED_CACHEIMAGE_TARGET
將生成cache.img
INSTALLED_VENDORIMAGE_TARGET
將生成vendor.img
INSTALLED_FILES_FILE
將生成install-files.txt,用于記錄當前系統中預裝的程序、庫等模塊
這幾個先決條件的生成原理類似,這里挑幾個做重點分析。
1.files
定義如下:
```mk
總結
以上是生活随笔為你收集整理的android系统源码的环境下用make来编译,Android源码编译系统入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 框架的特性_Go 语言 Web 框架 E
- 下一篇: 筛选出一证多卡 用sql_对比Excel