天天看點

《智能路由器開發指南》——2.2 編譯腳本分析

本節書摘來自異步社群《智能路由器開發指南》一書中的第2章,第2.2節,作者 張永智,李章明,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

openwrt代碼有8個固定的頂層目錄及6個編譯時建立的臨時目錄,頂層的固定目錄含義如表2-3所示。

《智能路由器開發指南》——2.2 編譯腳本分析

目錄config是編譯配置檔案目錄,是openwrt 15.05的新增目錄,是将一些編譯選項配置檔案分類放在這裡,包含全局編譯設定、開發人員編譯設定、目标檔案格式設定和核心編譯設定等4部分。

目錄include和scripts包含各種腳本和makefile。目錄target是指目标嵌入式裝置,針對不同的平台有不同的特性代碼。針對這些平台特性,“target/linux”目錄下按照平台進行目錄劃分,裡面包括了針對各種平台标準核心的更新檔及特殊配置等。目錄tools和toolchain包含了一些通用指令,用來生成固件、編譯器和c語言連結庫。目錄docs在編譯時不需要,用于存放開發文檔。目錄package則用于存放各種必要的軟體包。

編譯生成結果會儲存在以下3個目錄下:“build_dir/host”是一個臨時目錄,用來儲存不依賴于目标平台的工具;“build_dir/toolchain-”用來儲存依賴于指定平台的編譯工具鍊;“staging_dir/toolchain-”是編譯工具鍊的最終安裝位置。通常我們不需要改動編譯鍊目錄下的任何東西,除非要更新編譯工具版本等。

在openwrt固件中,幾乎所有東西都是軟體包(package),可以編譯為以“.ipk”結尾的安裝包,這樣就可以很友善地安裝、更新和解除安裝了。注意,擴充軟體包不是在主分支中維護的,但是可以使用軟體包編譯擴充機制(feeds)來進行擴充安裝。這些包能夠擴充基本系統的功能,隻需要将它們連結進入主幹。之後,這些軟體包将會顯示在編譯配置菜單中。

編譯工具鍊、目标平台的軟體包等需要下載下傳的檔案都放在dl目錄下。目标平台和軟體包兩部分都需要“build_dir/”作為編譯的臨時目錄,并且會将目錄staging_dir作為編譯的臨時安裝目錄,最終的生成檔案儲存在目錄bin下。

目錄feeds用于儲存擴充軟體包,可以使用軟體包編譯擴充機制來進行擴充安裝。這些包能夠擴充基本系統的功能,隻需要将它們連結進入編譯主目錄的package目錄下。之後,這些軟體包将會顯示在配置菜單中。編譯後生成6個臨時目錄,其含義如表2-4所示。

《智能路由器開發指南》——2.2 編譯腳本分析

目錄scripts為編譯工具腳本檔案,例如patch-kernel.sh封裝了patch指令,在編譯時,首先将patches目錄下的所有更新檔檔案打上,并且判斷如果打更新檔失敗将退出編譯過程。download.pl為下載下傳源代碼的工具腳本,封裝下載下傳工具wget的選項以及設定從哪裡下載下傳。表2-5所示為典型編譯腳本功能。目錄include用于儲存各種makefile檔案。

《智能路由器開發指南》——2.2 編譯腳本分析
《智能路由器開發指南》——2.2 編譯腳本分析

openwrt在建構時首先下載下傳代碼,就是使用scripts/download.pl腳本進行下載下傳,使用方法如下:

為下載下傳之後的儲存位置,下載下傳代碼通常均儲存在dl目錄下。

待下載下傳的檔案名。

下載下傳内容的md5,用于校驗下載下傳檔案是否正确。

為可選的參數,是下載下傳檔案的鏡像位址,可以有多個位址,優先選擇第一個,如果下載下傳失敗則順序選擇後面的位址。

該程式由perl語言開發出來,代碼并不複雜。代碼首先進行初始條件檢查,判斷參數是否足夠,至少需要3個參數分别為下載下傳檔案儲存位置、下載下傳檔案名及下載下傳内容md5值。 接着從指令行參數中順序讀取資料,并指派給局部變量,最後判斷md5sum或md5工具是否存在,如果不存在提示工具不存在後退出。

緊接着調用localmirrors()函數讀取本地的源碼鏡像位址,我們可以在企業内部建立自己的代碼鏡像伺服器,然後将鏡像位址放在“scripts/localmirrors”檔案中,這樣我們就不用每次編譯時都從網際網路上去下載下傳了。例如我這裡修改如下:

緊接着周遊指令行并将代碼中的鏡像位址加到備選鏡像中。最後使用while循環進行下載下傳,如果下載下傳完成就對下載下傳檔案的md5進行對比,如果md5值一緻則退出循環,否則進入下一個鏡像位址進行下載下傳。下載下傳成功後調用cleanup()函數來清理臨時變量。

這個下載下傳功能最重要的接口是我們可以通過“scripts/localmirrors”檔案自定義軟體包下載下傳位址,友善開發人員進行設定。

最近有很多iphone/android編譯工具爆出後門問題,就是因為使用其他第三方鏡像位址檔案來下載下傳編譯工具,但沒有對下載下傳的軟體内容進行md5值對比,進而導緻編譯的應用程式感染後門。openwrt的下載下傳檢查機制從源頭上解決了這類問題。在我開發openwrt時也發現了下載下傳的一些内容被感染的問題,但檢查機制丢棄了不正确的内容,從下一個的鏡像網站上繼續下載下傳。

openwrt的代碼包中大多均有patches目錄。下載下傳代碼包完成後進行打更新檔,采用的就是patck-kernel.sh腳本。腳本的第一個參數為編譯代碼目錄,第二個為更新檔目錄,調用腳本形式舉例如下。

執行流程如下。

(1)首先進行參數指派,第一個參數為代碼目錄,第二個參數為更新檔目錄。

(2)第二步判定代碼目錄和更新檔目錄是否存在,如果不存在則提示錯誤并退出。

(3)周遊更新檔檔案,根據字尾判斷更新檔檔案類型。

(4)調用patch指令應用更新檔。

(5)檢查更新檔應用是否正确,如果存在“*.rej”檔案表示出現錯誤,傳回“1”并退出。

(6)最後檢查如果存在應用更新檔後的備份檔案,則删除備份檔案。

傳統的linux作業系統在編譯某一個軟體的時候,會檢查其依賴軟體及頭檔案是否存在,如果沒有安裝,則會報缺少頭檔案或缺少連結庫等錯誤,編譯将退出。這種機制使得開發者在編譯一個軟體之前,需要查找該軟體所需的依賴庫及頭檔案,并手動去安裝這些軟體。有時候碰到比較嬌貴的軟體時,嵌套式的安裝依賴檔案,會使得開發者頭昏腦脹。openwrt通過引入feeds機制,很好地解決了這個問題。

feeds是openwrt開發所需要的軟體包套件的工具及更新位址集合,這些軟體包通過一個統一的接口位址進行通路。這樣使用者可以不用關心擴充包的存儲位置,可以減少擴充軟體包和核心代碼部分的耦合。它由兩部分組成,即擴充包位置配置檔案feeds.conf.default和腳本工具feeds。目前在配置檔案中儲存最重要的擴充軟體包集合有以下4個。

‘luci’openwrt預設的web浏覽器圖形使用者接口。

‘routing’一些額外的基礎路由器特性軟體,包含動态路由quagga等。

‘telephony’ip電話相關的軟體包,例如freeswitch和asterisk等。

‘management’tr069等各種管理軟體包。

當我們下載下傳了openwrt對應源碼之後,進行如下操作:

上述操作,就是利用feeds提供的接口将openwrt所需的全部擴充軟體包進行下載下傳并安裝。在更新時,需要能夠通路網際網路。在下載下傳之前可以通過檢視“feeds.conf.default”檔案,來檢查哪些檔案需要包含在編譯環境中。feeds工具用法如下。

update:下載下傳在feeds.conf或feeds.conf.default檔案中的軟體包清單并建立索引。-a表示更新所有的軟體包。隻有更新後才能進行後面的操作。

list:從建立的索引檔案“feed.index”中讀取清單并顯示。隻有進行更新之後才能檢視清單。

install:安裝軟體包以及它所依賴的軟體包,從feeds目錄安裝到package目錄,即在“package/feeds”目錄建立軟體包的軟連結。隻有安裝之後,在後面執行“make menuconfig”時,才可以對相關軟體包是否編譯進行選擇。

例如安裝luci-app-firewall:

search:按照給定的字元串來查找軟體包,需要傳入一個字元串參數。

uninstall:解除安裝軟體包,但它沒有處理依賴關系,僅僅删除本軟體包的軟連結。

clean:删除update指令下載下傳和生成的索引檔案,但不會删除install建立的連結。

feeds代碼處理過程是這樣的:這個指令首先讀取并解析feeds.conf配置檔案,然後執行相應的指令,例如install時,将安裝應用程式包和它所有直接或間接依賴的所有軟體包。安裝時将建立一個符号連結,從packages/feeds/$feed_name/$package_name指向feeds/$feed_name/$package_name, 這樣在“make menuconfig”時,feeds的軟體包就可以被處理到,就可以選擇編譯了。例如luci-app-firewall指向feeds/luci/applications/luci- app-firewall:

用一句話來說,編譯擴充安裝過程就是将feeds目錄下的軟體包連結到packages/feeds對應目錄下。可使用的feeds清單配置為feeds.conf或者feeds.conf.default。優先選擇feeds.conf檔案,這個檔案包含了擴充安裝源清單,每一行由3部分組成,包含feed方法、feed 名字和feed源。下面是一個擴充安裝源配置檔案的例子。

我們可以修改該檔案使編譯時從自己指定的位置進行下載下傳。主要支援feed方法的類型有以下3種。

src-cpy通過從資料源路徑複制資料。

src-git通過使用git從代碼倉庫位址下載下傳代碼資料。

src-svn通過使用svn從代碼倉庫位址下載下傳代碼資料。

繼續閱讀