OpenWrt構建過程(主Makefile分析)

玩了這麼久的OpenWrt,最近詳細研究了一下整個工程的構建過程,也但願做爲備份以便之後查閱。網上這方面的文章一大把,不過多數都只說起皮毛,我就選取了感受還不錯的放了過來,畢竟所有本身寫太麻煩了。我主要整理了下面這兩篇文章,和整個構建過程基本相符,在這裏也對原做者表示感謝。javascript

http://m.blog.csdn.net/article/details?id=50363519java

http://m.blog.csdn.net/article/details?id=50408104linux

編譯過程概述

編譯的整體過程以下:ruby

1.編譯host工具
2.編譯交叉工具鏈
3.編譯內核模塊
4.編譯ipk
5.安裝ipk到文件系統
6.編譯內核
7.將內核和文件系統組合成最終binary

1. 編譯host工具

雖然咱們在開始編譯前已經安裝了一些必要的工具,但編譯過程當中還須要其餘一些主機工具。這部分工具將首先編譯。bash

2. 編譯交叉工具鏈

openwrt自帶交叉編譯鏈,固然在編譯目標平臺軟件前,須要先編譯。markdown

3. 編譯內核模塊

由於部份內核模塊將會生成獨立的ipk,因此內核模塊須要首先編譯。ide

4. 編譯ipk

這裏將編譯package目錄下的各個軟件包,這也是和咱們最爲息息相關的。函數

5. 安裝ipk

將生成的ipk安裝到文件系統之中(好比build_dir/target-XXX/root-ramips目錄)。工具

6. 編譯內核

在完成ipk編譯以後,將會編譯內核,壓縮內核.同時使用mkimage工具,在內核前面生成一個用於uboot識別的頭部。ui

7. 合成

在最後一步,將文件系統和內核鏈接在一塊兒,即生成了目標二進制鏡像文件。

 

Makefile結構分析

咱們以chaos calmer的代碼爲例,整個編譯的入口是在源碼根目錄下的Makefile。編譯的各類命令都應該在源碼根目錄下鍵入。
整個主Makefile的結構以下:

world:
ifneq ($(OPENWRT_BUILD),1)
    頂層
else
    第二層
endif

開始部分是一些註釋和變量定義及路徑檢查。
根據Makefile的規則,在沒有指定編譯目標的時候,Makefile中的第一個目標將做爲默認目標。
換句話說,當咱們執行make V=s時,這個時候編譯的目標就是world.和咱們執行make world V=s效果是同樣的。

頂層

一般在編譯時,咱們不會定義變量OPENWRT_BUILD的值,因此一般咱們是會走到頂層的。
頂層代碼以下:

_SINGLE=export MAKEFLAGS=$(space);

  override OPENWRT_BUILD=1
  export OPENWRT_BUILD
  GREP_OPTIONS=
  export GREP_OPTIONS
  include $(TOPDIR)/include/debug.mk
  include $(TOPDIR)/include/depends.mk
  include $(TOPDIR)/include/toplevel.mk

這裏咱們看到變量OPENWRT_BUILD被置爲1,而後包含了3個.mk文件。
這裏稍微解釋下.mk文件,它們通常沒有什麼執行動做,都是一些變量的定義還有依賴關係的說明,能夠類比於C語言的頭文件來理解。

debug.mk:

能夠經過定義DEBUG的值來控制編譯過程。

depends.mk:

主要定義了rdep這個變量。

toplevel.mk:

這個是咱們跟蹤編譯過程的重要的文件,這個文件在源碼根目錄下的include文件夾下。

核心代碼以下:

 1 %::
 2     @+$(PREP_MK) $(NO_TRACE_MAKE) -r -s prereq
 3     @( \
 4         cp .config tmp/.config; \
 5         ./scripts/config/conf --defconfig=tmp/.config -w tmp/.config Config.in > /dev/null 2>&1; \
 6         if ./scripts/kconfig.pl '>' .config tmp/.config | grep -q CONFIG; then \
 7             printf "$(_R)WARNING: your configuration is out of sync. Please run make menuconfig, oldconfig or defconfig!$(_N)\n" >&2; \
 8         fi \
 9     )
10     @+$(ULIMIT_FIX) $(SUBMAKE) -r $@ $(if $(WARN_PARALLEL_ERROR), || { \
11         printf "$(_R)Build failed - please re-run with -j1 to see the real error message$(_N)\n" >&2; \
12         false; \
13     } )

 

除了少數在toplevel中被定義的目標外,其餘編譯目標都會走到這裏,將之簡化後(執行命令爲: make V=s):

%::
    @make V=s -r -s prereq
    @make -w -r world

 

首先執行prereq,而後再執行咱們指定的目標或者默認目標world

prereq整理後的依賴關係以下:
prereq
其中,staging_dir/host/.prereq-build:

將會執行一系列主機檢查,是否安裝了必要的軟件。

prepare-tmpinfo:

根據scan.mk,掃描target/linuxpackage目錄,生成packageinfo和targetinfo。

總之,頂層完成一系列必要的準備工做.對於絕大多數的目標而言,頂層是必經之路。固然,在toplevel.mk中,咱們也能夠看到目標menuconfig。也就是說對於目標menuconfig而言,將不會執行到第二層的邏輯。

第二層

在上面執行完make prereq以後,將執行make world。
還記得咱們進入頂層後修改了變量OPENWRT_BUILD麼?當再次執行make world的時候,因爲條件不知足,咱們將直接進入第二層來執行。

include rules.mk
  include $(INCLUDE_DIR)/depends.mk
  include $(INCLUDE_DIR)/subdir.mk
  include target/Makefile
  include package/Makefile
  include tools/Makefile
  include toolchain/Makefile

rules.mk:

很重要的一個mk文件,其中規定了不少有用的變量,包括各類目錄路徑的定義,交叉編譯器等等。其中:

ifeq ($(DUMP),)
  -include $(TOPDIR)/.config
endif

就是包含了咱們的配置文件。對於Makefile而言,.config文件就是一大串變量的定義,Makefile能夠直接讀取這些定義,從而控制編譯過程。

subdir.mk:

這個是讀懂咱們整個編譯過程的關鍵所在,其中主要定義了兩個函數:subdirstampfile,咱們稍後加以解釋。

接下來,包含了4個Makefile文件。咱們以target/Makefile爲例.該文件位於target目錄下。
核心部分爲:

$(eval $(call stampfile,$(curdir),target,prereq,.config))
$(eval $(call stampfile,$(curdir),target,compile,$(TMP_DIR)/.build))
$(eval $(call stampfile,$(curdir),target,install,$(TMP_DIR)/.build))

$(eval $(call subdir,$(curdir)))

這裏調用了subdir.mk中定義的stampfile函數。將會生成target/stamp-prereq,target/stamp-compile,target/stamp-install三個變量。
target/stamp-prereq爲例,執行部分爲make target/prereq。同理target/stamp-compile,執行部分爲make target/compile

最後又調用了sbudir函數,這個函數規定了目標和各子文件夾之間的依賴關係。若是有必定的Makefile基礎能夠去讀讀subdir.mk文件。
舉例而言就是:

當執行目標爲target/compile,這個目標將依賴於target/linux/compile
當執行目標爲package/compile,這個目標將依賴於package目錄下各子文件夾的compile

下面就是規定了一系列的依賴關係,我已經將他們梳理了出來,以下圖:
world

 

編譯過程當中的一些變量可能會形成一些困擾,這裏將它們的真實值記錄下來,以執行make V=s爲例:

1 $(PREP_MK) => OPENWRT_BUILD= QUIET=0
2 
3 $(NO_TRACE_MAKE) => make V=ss
4 
5 $(_SINGLE) => export MAKEFLAGS= ;
6 
7 $(ULIMIT_FIX) => _limit=1024; [  = unlimited -o  -ge 1024 ] || ulimit -n 1024;
相關文章
相關標籤/搜索