(四) openwrt單個ipk編譯過程


Tags : Makefile


本周是成胖子每周一博的第五周.
更好的閱讀體驗,請點擊這里


前言

前一篇博客中,我們已經知道整個openwrt的編譯順序,本文我們來探討與開發者息息相關的單個ipk的編譯過程.在開發者進行二次開發的時候,我們既可以單個編譯ipk也可以完整編譯整個鏡像文件.在完整編譯的時候,我們選中的單個ipk同樣會被編入鏡像文件中,所以完整編譯同樣會進行單個ipk包的編譯.

我們前面在stampfile函數部分提高過,當編譯目標為package/stamp-compile的時候,實際執行的目標為package/compile;同時根據subdir函數的定義,package/compile將會依賴於package文件夾下被make menuconfig選中的子文件夾的compile.簡而言之,當我們執行make package/compile相當於對所有選中的文件夾執行make package/XXX/compile.


ipk Makefile分析

我們以一個具體的包的編譯過程來看看,本文我們以package/network/services/dropbear這個包為例.當我們在命令行中輸入make package/network/services/dropbear/compile的時候,make將會讀入dropbear下的Makefile文件,同時目標指定為compile.[1]

因為空間問題,我在這里不展開具體的Makefile文件.相信能看這篇博客的同學應該都有源碼,自己打開便是.
下面我們根據GNU make語法來分析這個Makefile文件.它包含了兩個.mk文件:一個是rules.mk,另一個是package.mk.

rules.mk
這個文件我們前文已經提到過了,主要是大量變量的定義.包括各種路徑的定義,編譯器的定義等等.其中要說明的是 .config文件也是這里被包含進來的.
package.mk
這個文件首先定義和補充了一些變量.其次是 openwrt為我們封裝了 BuildPackage函數,對於普通開發者而言,只需要參照模板定義相應的變量,最后調用這個函數即可.

其余的我們可以認為是變量的賦值語句,很明顯使用它們的地方並不在這里.關於模板和變量值的說明及作用.,你可以參照官方說明,也可以在網上找到一大堆資料.

最后,最重要的語句是這一句:

$(eval $(call BuildPackage,dropbear))

這里將會把dropbear作為參數值傳給函數BuildPackage

Tips
不知道大家還記得我們Makefile的執行順序么?Makefile是先讀入所有信息,展開,然后生成依賴關系.最后再按依賴關系先后來執行.

依賴關系

BuildPackage分析

BuildPackage的定義在package.mk中,定義如下:

define BuildPackage
  $(Build/IncludeOverlay)
  $(eval $(Package/Default))
  $(eval $(Package/$(1)))

ifdef DESCRIPTION
$$(error DESCRIPTION:= is obsolete, use Package/PKG_NAME/description)
endif

ifndef Package/$(1)/description
define Package/$(1)/description
	$(TITLE)
endef
endif

  BUILD_PACKAGES += $(1)
  $(STAMP_PREPARED): $$(if $(QUILT)$(DUMP),,$(call find_library_dependencies,$(DEPENDS)))

  $(foreach FIELD, TITLE CATEGORY SECTION VERSION,
    ifeq ($($(FIELD)),)
      $$(error Package/$(1) is missing the $(FIELD) field)
    endif
  )

  $(if $(DUMP), \
    $(Dumpinfo/Package), \
    $(foreach target, \
      $(if $(Package/$(1)/targets),$(Package/$(1)/targets), \
        $(if $(PKG_TARGETS),$(PKG_TARGETS), ipkg) \
      ), $(BuildTarget/$(target)) \
    ) \
  )
  $(if $(PKG_HOST_ONLY)$(DUMP),,$(call Build/DefaultTargets,$(1)))
endef

那么這里的$(1)就是指的傳入的參數dropbear.這里包含了一些檢查和補充變量定義.繼續深究下去的線索是第25~32行之間.這里我將它簡化后就是展開BuildTarget/ipkg;同時第33行,將dropbear當作參數傳給函數Build/DefaultTargets.

BuildTarget/ipkg定義在package-ipkg.mk中,我們需要重點關注其中的冒號,這個形成我們的依賴關系.
Build/DefaultTargets定義在package.mk中,其中形成了我們stamp-*的依賴關系.根據這些依賴關系,我將關系圖繪制如下:
package/compile

執行

當我們得出依賴關系后,執行過程就是倒序進行而已,即從上圖的右邊向左執行.這也可以和我們預料的執行過程相印證.

$(stamp-prepared)
主要完成代碼包的准備工作,如果開發者定義了 build/prepare,則執行 build/prepare.如果開發者未定義,則執行 build/prepare/default這其中包含了多個情形,最為常見的是將 dl下的壓縮包解壓並打上patch.
$(stamp-built)
這個將會進入到 build_dir/target-XXX/下對應的文件夾進行編譯.同時將會帶入一些定義好的變量.比如CFLAGS,LDFLAGS.
IPKG_(1)
這個目標將會將編譯好的文件安裝到對應的 ipkg-arch目錄下,同時將這個目錄打包為ipk文件.

尾記

本周博客基本就到這里,本來私心想着元旦沒啥大事,可以寫兩篇的.結果混着混着就到第三天晚上了.剩下的最后一篇我們看看單個的ipk編譯好了,內核的編譯過程,最后的打包過程.整個鏡像文件由哪些部分組成.下周再見.


  1. 全部過程分析請參見前文,至少還應包含必要的檢查,tools和toolchain的編譯.這里因為我們主要分析單個ipk的編譯過程,所以我將之略過 ↩︎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM