2020年10月21日 星期三

使用Buildroot創建小型Linux映像

有許多嵌入式Linux發行版可以使用,它們都具有良好的功能,但要犧牲映像大小,有些映像可能會達到4GB。有時,我們希望嵌入式系統僅支持系統所需的最小軟件包來支持Linux,例如運行沒有圖形界面的小型FTP服務器。

為了實現獲得自定義映像的目的,我們可以通過一些用於基於Linux嵌入式系統的構建工具來自動化並促進構建發行版。今天,已有的一些開源項目包括YoctoBuildroot

在此博客文章中,我們將評論如何使用Buildroot以及如何使用它為RaspberryPi3創建自定義圖像整個示例在此處提供

Buildroot

Buildroot是用於自動創建嵌入式Linux發行版的工具。它建立了電路板架構的代碼,因此通過對Makefile的概述來進行設置。除了開源之外,它還根據GPL-2.0或更高版本獲得許可

如何安裝

在開始安裝Buildroot之前,讓我們假設您已經準備好用於構建C項目的Linux環境,以及用於git,svn和rsync的工具。

您可以通過Buildroot官方文檔獲得有關需求的更多信息

為了安裝Buildroot,我們將通過Github克隆存儲庫:

$ git clone https://github.com/buildroot/buildroot.git buildroot

分析Buildroot

進入Buildroot目錄後,我們將有以下樹:

buildroot/
|
├── arch
├── board
├── boot
├── configs
├── docs
├── linux
├── package
├── support
├── system
├── toolchain
└── utils

每個目錄都包含設置一部分構建所需的一組文件。在這裡我們可以突出顯示:

  • board:包含用於目標板映射和配置的文件,例如閃存地址和設備樹文件;
  • configs:包含一系列預設配置,以自動將哪些軟件包和屬性添加到嵌入式映像;
  • 軟件包:包含到目前為止Buildroot可用的所有官方軟件包。我們不僅限於這些軟件包,Buildroot允許我們創建新的自定義軟件包。

我們將更加關注package文件夾,因為我們的主要興趣是自定義安裝在映像中的包。

作為示例,讓我們看一下fmt軟件包,其中包含3個文件:

package/fmt/
|
├── Config.in
├── fmt.hash
└── fmt.mk

Config.in是用於Buildroot配置的軟件包描述,它在選擇要構建的軟件包時負責維護用戶界面的信息。它還包含程序包依賴性。

config BR2_PACKAGE_FMT
    bool "fmt"
    depends on BR2_INSTALL_LIBSTDCPP
    depends on BR2_USE_WCHAR
    help
      fmt is an open-source formatting library for C++. It can be
      used as a safe alternative to printf or as a fast alternative
      to IOStreams.

      https://fmt.dev/latest/index.html

fmt.mk文件是在Makefile配方建立,建設和安裝庫。

FMT_VERSION = 5.3.0
FMT_SITE = $(call github,fmtlib,fmt,$(FMT_VERSION))
FMT_LICENSE = BSD-2-Clause
FMT_LICENSE_FILES = LICENSE.rst
FMT_INSTALL_STAGING = YES

FMT_CONF_OPTS = \
    -DHAVE_OPEN=ON \
    -DFMT_INSTALL=ON \
    -DFMT_TEST=OFF

$(eval $(cmake-package))

該文件存儲所有其他軟件包的默認屬性,例如其版本,從中下載源代碼的站點,軟件許可證名稱以及在何處可以找到該許可證文件。

在這裡,您可以看到最後調用了一個名為cmake-package的模塊這個模塊負責使用CMake處理項目,它將執行從配置到工件安裝的所有必要命令。這種模塊化允許更高水平的自動化,否則有必要描述每個軟件包的所有CMake命令。

最後一個文件fmt.hash包含直接從站點下載的文件的校驗和。

sha256 defa24a9af4c622a7134076602070b45721a43c51598c8456ec6f2c4dbb51c89  fmt-5.3.0.tar.gz
sha256 560d39617dfb4b4e4088597291a070ed6c3a8d67668114ed475c673430c3e49a  LICENSE.rst

儘管我們使用的是SHA-256,但Buildroot能夠支持SHA-1和MD5等其他格式。軟件包下載期間,Buildroot會自動驗證校驗和。如果找到的值與描述的值不同,將引發錯誤。

配置自定義映像

由於我們的目標平台是RaspberryPi3,因此Buildroot為此板提供了一個預先配置的文件,該文件位於configs目錄中。

要詢問Buildroot,我們要從RaspberryPi3構建我們的配置,我們應該使用以下命令:

$ make raspberrypi3_defconfig

一旦執行,此命令將生成文件.config,其中包含映像所需的所有軟件包,內核,工具鍊和屬性。要添加新程序包或編輯現有程序包,我們需要處理此文件,但這不是非常自動化,會在構建過程中導致許多錯誤。這就是Buildroot具有更多用戶友好界面的原因,您可以在其中自定義最終配置並自動解決依賴性。該界面有不同的格式,您可以嘗試其中的一些格式:

$ make config
$ make menuconfig
$ make gconfig
$ make xconfig

在此示例中,我們將使用menuconfig,因為它具有最小的圖形界面,並且不需要Qt之類的其他系統依賴項。

執行配置命令後,我們將得到以下輸出:

正如我們之前已經詳細介紹了fmt庫,我們將其包含在圖像中,因此我們必須以以下方式瀏覽菜單:

Target Packages -> Libraries -> Text and terminal handling -> fmt

要獲取有關包裝的更多信息,可以輸入它將顯示與Config.in文件中相同的內容選擇後,我們可以通過面板保存當前設置,然後按ESC退出

建立形象

設置完成後,我們可以繼續執行本教程中最長的步驟,構建映像。儘管構建只是命令,但Buildroot將必須下載配置文件中存在的所有源,從源進行構建,最後生成自定義映像。要開始構建過程,只需運行:

$ make

從現在開始,Buildroot將負責整個構建過程,第一次可能需要幾個小時。對於將來的構建,可以重新使用緩存,這會將構建時間縮短到幾分鐘。

使用柯南進行Buildroot構建

儘管Buildroot可以通過其軟件包結構接受新軟件包,但是構建過程仍然有些長,但是第一次可能要花費幾個小時。但是,如果可以通過下載預構建的軟件包將這一過程減少到幾分鐘,該怎麼辦?讓我們看一下柯南的一些與場景相關的功能和方面:

  • 能夠提供所有平台和配置的所有二進製文件的統一視圖,而不僅僅是buildroot;
  • 開發人員可以快速開發,並在其Linux盒中使用本機二進製文件在本地進行測試;
  • 由於重複使用了二進製文件,因此構建速度更快,不僅用於開發,而且還用於生產和發布;
  • 最佳DevOps最佳實踐,避免多次從源代碼重建二進製文件。

在將柯南引入Buildroot之前,我們需要了解如何與buildroot協作以進行軟件包構建的腳本結構:

$ ls package/*.mk
package/doc-asciidoc.mk   package/pkg-cmake.mk  package/pkg-download.mk  package/pkg-golang.mk
...

Makefile的此列表負責為每個給定的軟件包執行構建過程。回到ZLib庫的配方示例,我們有以下部分:

$(eval $(cmake-package))

此行告訴我們,該pkg-cmake.mk腳本將用於構建ZLib項目。在集成Conan的情況下,我們將必鬚根據Buildroot給出的配置,使用負責下載所需軟件包的命令構建一個新腳本,並將工件複製到它們的正確位置。

將Conan與Buildroot集成

讓我們目錄中創建一個名為pkg-conan.mk的新文件package/同時,我們需要將其添加到package/Makefile.in文件中,以便Buildroot能夠列出它。

$ echo 'include package/pkg-conan.mk' >> package/Makefile.in

對於腳本開發,我們將其分為幾個步驟。因為它是一個大文件,所以在本文中我們將僅描述它的一部分,但是完整版本可以在此處找到

Buildroot通過變量定義其設置,包括處理器,編譯器版本和構建類型。但是,這些變量沒有針對柯南的直接有效值,因此我們需要解析它們中的大多數。讓我們從編譯器版本開始,默認情況下Buildroot使用基於GCC的工具鏈,因此我們僅過濾其可能的版本:

CONAN_SETTING_COMPILER_VERSION  ?=
ifeq ($(BR2_GCC_VERSION_8_X),y)
CONAN_SETTING_COMPILER_VERSION = 8
else ifeq ($(BR2_GCC_VERSION_7_X),y)
CONAN_SETTING_COMPILER_VERSION = 7
else ifeq ($(BR2_GCC_VERSION_6_X),y)
CONAN_SETTING_COMPILER_VERSION = 6
else ifeq ($(BR2_GCC_VERSION_5_X),y)
CONAN_SETTING_COMPILER_VERSION = 5
else ifeq ($(BR2_GCC_VERSION_4_9_X),y)
CONAN_SETTING_COMPILER_VERSION = 4.9
endif

對於build_type,arch等,應重複相同的過程。對於柯南軟件包安裝步驟,我們將執行以下例程:

define $(2)_BUILD_CMDS
    $$(TARGET_MAKE_ENV) $$(CONAN_ENV) $$($$(PKG)_CONAN_ENV) \
        CC=$$(TARGET_CC) CXX=$$(TARGET_CXX) \
        $$(CONAN) install $$(CONAN_OPTS) $$($$(PKG)_CONAN_OPTS) \
        $$($$(PKG)_REFERENCE) \
        -s build_type=$$(CONAN_SETTING_BUILD_TYPE) \
        -s arch=$$(CONAN_SETTING_ARCH) \
        -s compiler=$$(CONAN_SETTING_COMPILER) \
        -s compiler.version=$$(CONAN_SETTING_COMPILER_VERSION) \
        -g deploy \
        --build $$(CONAN_BUILD_POLICY)
endef

Conan安裝命令將照常執行,但是設置和選項是通過以前從Buildroot收集的內容進行配置的,並通過Buildroot軟件包配方接受新的設置和選項。因為在這種情況下,以前所有資源都是在第一時間進行編譯的,所以我們將Conan的構建策略設置為missing,因此將構建任何不可用的軟件包。

另外,請注意,我們正在使用generator deploy,因為我們需要將所有工件複製到Buildroot內部結構中。構建完成後,我們將通過以下例程複製庫,可執行文件和頭文件:

define $(2)_INSTALL_CMDS
    cp -f -a $$($$(PKG)_BUILDDIR)/bin/. /usr/bin 2>/dev/null || :
    cp -f -a $$($$(PKG)_BUILDDIR)/lib/. /usr/lib 2>/dev/null || :
    cp -f -a $$($$(PKG)_BUILDDIR)/include/. /usr/include 2>/dev/null || :
endef

通過此腳本,我們將能夠使用每個Buildroot配方的更簡單信息來安裝絕大多數Conan軟件包。

安裝柯南Zlib

一旦有了用於安裝Conan軟件包的腳本,現在讓我們安裝一個相當簡單且眾所周知的項目:zlib為此,我們將在包目錄中創建一個新配方。讓我們從包配置文件開始:

mkdir package/conan-zlib
touch package/conan-zlib/Config.in
touch package/conan-zlib/conan-zlib.mk

Config.in文件的內容應如下所示:

config BR2_PACKAGE_CONAN_ZLIB
    bool "conan-zlib"
    help
      Standard (de)compression library. Used by things like
      gzip and libpng.

      http://www.zlib.net

現在讓我們轉到包含Zlib數據conan-zlib.mk

# conan-zlib.mk
CONAN_ZLIB_VERSION = 1.2.11
CONAN_ZLIB_LICENSE = Zlib
CONAN_ZLIB_LICENSE_FILES = licenses/LICENSE
CONAN_ZLIB_SITE = $(call github,conan-community,conan-zlib,92d34d0024d64a8f307237f211e43ab9952ef0a1)
CONAN_ZLIB_REFERENCE = zlib/$(CONAN_ZLIB_VERSION)@conan/stable

$(eval $(conan-package))

這裡重要的一點CONAN_ZLIB_SITE是即使不用於我們的目的也是必需的。如果不存在,則Buildroot將在執行過程中引發錯誤。其他變量很簡單,僅表示軟件包參考,名稱,版本和許可證。請注意,最後我們調用了應該執行柯南的腳本。

創建後,我們仍然需要將其添加到Buildroot配置列表中。為此,我們使用名為的新菜單更新列表Conanpackage / Config.in文件中,讓我們添加以下部分:

menu "Conan"
    source "package/conan-zlib/Config.in"
endmenu

現在,只需通過menuconfig選擇軟件包:

Target Packages -> Conan -> conan-zlib

配置並保存後,只需make再次運行即可安裝該軟件包。在安裝過程中,我們將顯示以下輸出:

Buildroot構建

如您所見,Conan遵循的是Buildroot所使用的配置文件,這為我們提供了無需手動創建配置文件的優勢。

在安裝結束時,它將被複製到輸出目錄。

自定義柯南遙控器

假設我們有一個Artifactory實例,其中所有軟件包都可以下載。我們如何定制Buildroot使用的遙控器?我們需要引入一個新選項,我們可以在其中寫入遠程名稱,而柯南將可以使用該變量。首先,我們需要創建一個新的配置文件,以便在柯南菜單中插入新選項:

$ mkdir package/conan
$ touch package/conan/Config.in

Config.in文件應包含:

config CONAN_REMOTE_NAME
	string "Conan remote name"
    help
	  Look in the specified remote server.

另外,我們需要CONAN_REMOTE_NAMEpkg-conan.mk中解析該選項並將其添加到Conan命令行中:

ifneq ($(CONAN_REMOTE_NAME),"")
CONAN_REMOTE = -r $$(CONAN_REMOTE_NAME)
endif

...

define $(2)_BUILD_CMDS
    $$(TARGET_MAKE_ENV) $$(CONAN_ENV) $$($$(PKG)_CONAN_ENV) \
        CC=$$(TARGET_CC) CXX=$$(TARGET_CXX) \
        $$(CONAN) install $$(CONAN_OPTS) $$($$(PKG)_CONAN_OPTS) \
        $$($$(PKG)_REFERENCE) \
        -s build_type=$$(CONAN_SETTING_BUILD_TYPE) \
        -s arch=$$(CONAN_SETTING_ARCH) \
        -s compiler=$$(CONAN_SETTING_COMPILER) \
        -s compiler.version=$$(CONAN_SETTING_COMPILER_VERSION) \
        -g deploy \
        --build $$(CONAN_BUILD_POLICY) \
        $$(CONAN_REMOTE)
endef

現在,我們準備設置我們的特定遠程名稱。我們只需要運行make menuconfig並遵循以下路徑:

Target Packages -> Libraries -> Conan -> Conan remote name

我們將看到:

Buildroot構建

現在,Conan已配置為在遠程命名工件中搜索軟件包但是我們需要make再次運行請注意,由於現在有了Buildroot提供的緩存,因此構建時間將減少。現在我們準備好進行最後一步了。

安裝映像

經過兩個小時和幾杯咖啡後,如果在此過程中沒有發生錯誤,我們將得到以下輸出:

$ ls output/images/
  bcm2710-rpi-3-b.dtb bcm2710-rpi-3-b-plus.dtb bcm2710-rpi-cm3.dtb boot.vfat rootfs.ext2 rootfs.ext4 rpi-firmware sdcard.img zImage

$ ls -lh output/images/sdcard.img
    -rw-r--r-- 1 conan conan 153M ago  6 11:43 output/images/sdcard.img

這些工件是在構建過程中生成的所有內容的最終編譯,這裡我們將對sdcard.img文件感興趣這是我們將在RaspberryPi3上使用的最終圖像,它只有153MB。與Raspbian等其他嵌入式發行版相比,它要小得多。

現在,將圖像複製到目標SD卡:

$ sudo dd if=output/images/sdcard.img of=/dev/mmcblk0 bs=4M conv=sync status=progress

請記住,SD卡的安裝點可能會因您的分佈而異。

完成後,將SD卡插入RaspberryPi3,並為已連接到視頻輸出的卡供電。您將看到引導加載程序正常運行,最後將顯示登錄屏幕,默認用戶為root,無需密碼。

結論

在本文中,我們討論瞭如何使用Buildroot輕鬆創建Linux發行版,從而獲得非常精簡的映像。

Buildroot僅使用感興趣的開發人員軟件包來幫助自動創建自定義嵌入式Linux發行版。

儘管構建過程可能需要幾個小時,但隨著柯南集成並替換了一些軟件包,該時間可以減少到只有幾分鐘。 

資料來源: https://blog.conan.io/2019/08/27/Creating-small-Linux-images-with-Buildroot.html

沒有留言: