2020年12月15日 星期二

buildroot使用介紹

buildroot是Linux平台上一個構建嵌入式Linux系統的框架。整個Buildroot是由Makefile腳本和Kconfig配置文件構成的。你可以和編譯Linux內核一樣,通過buildroot配置,menuconfig修改,編譯出一個完整的可以直接燒寫到機器上運行的Linux系統軟件(包含boot、kernel、rootfs以及rootfs中的各種庫和應用程序) 。

使用buildroot搭建基於qemu的虛擬開發平台,參考《通過buildroot+qemu搭建ARM-Linux虛擬開發環境》。

首先如何使用buildroot,1.選擇一個defconfig;2.根據需要配置buildroot;3.編譯buildroot;4.在qemu或者目標板上運行buildroot構建的系統。

1.1 buildroot目錄介紹

進入buildroot首先映入眼簾的是一系列目錄,簡要介紹如下:

複製代碼

.
├── arch:存放CPU架構相關的配置腳本,如arm/mips/x86,這些CPU相關的配置,在製作工具鏈時,編譯uboot和kernel時很關鍵.
├── board
├── boot
├── CHANGES
├── Config. in 
├── Config. in .legacy
├── configs:放置開發板的一些配置參數. 
├── COPYING
├── DEVELOPERS
├── dl:存放下載的源代碼及應用軟件的壓縮包.  
├── docs:存放相關的參考文檔.  
├── fs:放各種文件系統的源代碼.  
├── linux:存放著Linux kernel的自動構建腳本. 
├── Makefile
├── Makefile.legacy
├── output:是編譯出來的輸出文件夾.  
│ ├── build:存放解壓後的各種軟件包編譯完成後的現場. 
│ ├── host:存放著製作好的編譯工具鏈,如gcc 、arm-linux-gcc等工具. 
│ ├── images:存放著編譯好的uboot.bin, zImage, rootfs等鏡像文件,可燒寫到板子裡,讓linux系統跑起來. 
│ ├── staging 
│ └── target:用來製作rootfs文件系統,裡面放著Linux系統基本的目錄結構,以及編譯好的應用庫和bin可執行文件. (buildroot根據用戶配置把.ko .so .bin文件安裝到對應的目錄下去,根據用戶的配置安裝指定位置) 
├── package:下面放著應用軟件的配置文件,每個應用軟件的配置文件有Config.in和soft_name.mk。
├── README
├── support
├── system
└── toolchain

複製代碼

1.2 buildroot配置

通過make xxx_defconfig來選擇一個defconfig,這個文件在config目錄下。

然後通過make menuconfig進行配置。

複製代碼

Target options   --->選擇目標板架構特性。
Build options   --->配置編譯選項。
Toolchain   --->配置交叉工具鏈,使用buildroot工具鏈還是外部提供。
System configuration   ---> 
Kernel   ---> 
Target packages   ---> 
Filesystem images   ---> 
Bootloaders   ---> 
Host utilities   ---> 
Legacy config options   --->

複製代碼

1.3 make命令使用

通過make help可以看到buildroot下make的使用細節,包括對package、uclibc、busybox、linux以及文檔生成等配置。

複製代碼

Cleaning:
  clean                   - delete all files created by build
  distclean               - delete all non- source files (including .config)

Build:
  all                     - make world
  toolchain               - build toolchain

Configuration:
  menuconfig              - interactive curses- based configurator--------------------------------對整個buildroot進行配置
  savedefconfig           - Save current config to BR2_DEFCONFIG (minimal config)----------------保存menuconfig的配置

Package - specific:--------------------- -------------------------------------------------- --------對package配置
  <pkg> - Build and install <pkg> and all its dependencies---------------------單獨編譯對應APP
   <pkg>-source - Only download the source files for <pkg>
  <pkg>-extract - Extract <pkg> sources
   <pkg>-patch - Apply patches to <pkg>
  <pkg>-depends - Build <pkg> ' s dependencies 
  <pkg>-configure - Build <pkg> up to the configure step
   <pkg>-build - Build <pkg> up to the build step
   <pkg>-show-depends - List packages on which <pkg> depends
   <pkg>-show-rdepends - List packages which have <pkg> as a dependency
   <pkg>-graph-depends - Generate a graph of <pkg> ' s dependencies 
  <pkg>-graph -rdepends - Generate a graph of <pkg> ' s reverse dependencies 
  <pkg>-dirclean - Remove <pkg>build directory-----------------------------------------清除對應APP的編譯目錄
  <pkg>-reconfigure - Restart the build from the configure step
   <pkg>-rebuild - Restart the build from the build step------------------------ --------單獨重新編譯對應APP

busybox:
  busybox -menuconfig - Run BusyBox menuconfig

uclibc:
  uclibc -menuconfig - Run uClibc menuconfig

linux:
  linux -menuconfig - Run Linux kernel menuconfig-----------------------------------------配置Linux並保存設置
  linux -savedefconfig - Run Linux kernel savedefconfig
  linux -update-defconfig - Save the Linux configuration to the path specified
                             by BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE

Documentation:
  manual                  - build manual in all formats
   manual -pdf - build manual in PDF
   graph -build - generate graphs of the build times------------------------- ---------對編譯時間、編譯依賴、文件系統大小生成圖標
  graph -depends - generate graph of the dependency tree
  graph -size - generate stats of the filesystem size

複製代碼

 Buildroot提供了函數框架和變量命令框架(下一篇文章將介紹細節),採用它的框架編寫的app_pkg.mk這種Makefile格式的自動構建腳本,將被package/pkg-generic.mk 這個核心腳本展開填充到buildroot主目錄下的Makefile中去。

最後make all執行Buildroot主目錄下的Makefile,生成你想要的image。package/pkg-generic.mk中通過調用同目錄下的pkg-download.mk、pkg-utils.mk文件,已經幫你自動實現了下載、解壓、依賴包下載編譯等一系列機械化的流程。

你只要需要按照格式寫Makefile腳app_pkg.mk,填充下載地址,鏈接依賴庫的名字等一些特有的構建細節即可。總而言之,Buildroot本身提供構建流程的框架,開發者按照格式寫腳本,提供必要的構建細節,配置整個系統,最後自動構建出你的系統。

對buildroot的配置通過Config.in串聯起來,起點在根目錄Config.in中。

配置選項Config.in位置 
Target optionsarch/Config.in 
Build optionsConfig.in 
Toolchaintoolchain/Config.in 
System configurationsystem/Config.in 
Kernellinux/Config.in 
Target packagespackage/Config.in 
Target packages->Busybox  
Filesystem imagesfs/Config.in 
Bootloadersboot/Config.in 
Host utilitiespackage/Config.in.host 
Legacy config optionsConfig.in.legacy 
   

對Linux內核的配置包括兩部分:通過make menuconfig進入Kernel對內核進行選擇,通過make linux-menuconfig對內核內部進行配置。

3.1 選擇Linux內核版本

如下“Kernel version”選擇內核的版本、“Defconfig name”選擇內核config文件、“Kernel binary formant”選擇內核格式、“Device tree source file names”選擇DT文件,

在“Linux Kernel Tools”中選擇內核自帶的工具,比如perf。

可以選擇“Custom Git repository”來指定自己的Git庫,在“Custom repository version”中指定branch名稱。

選擇“Using an in-tree defconfig file”,在“Defconfig name”中輸入defconfig名稱,注意不需要末尾_defconfig。

選擇“Use a device tree present in the kernel”,在“Device Tree Source file names”中輸入dts名稱,不需要.dts擴展名。

3.1.1 Kernel binary format

可以選擇vmlinux或者uImage。

uImage是uboot專用的映像文件,它是在zImage之前加上一個長度為64字節的“頭”,說明這個內核的版本、加載位置、生成時間、大小等信息;其0x40之後與zImage沒區別。 

zImage是ARM Linux常用的一種壓縮映像文件,uImage是U-boot專用的映像文件,它是在zImage之前加上一個長度為0x40的“頭”,說明這個映像文件的類型、加載位置、生成時間、大小等信息。

vmlinux編譯出來的最原始的內核elf文件,未壓縮。

zImage是vmlinux經過objcopy gzip壓縮後的文件, objcopy實現由vmlinux的elf文件拷貝成純二進制數據文件。

uImage是U-boot專用的映像文件,它是在zImage之前加上一個長度為0x40的tag。 

 選擇vmlinux和uImage的區別在於:

PATH= " /bin... " BR_BINARIES_DIR=/home/.../output/images /usr/bin/make -j9 HOSTCC= " /usr/bin/gcc " HOSTCFLAGS= "" ARCH=csky INSTALL_MOD_PATH=/home /.../output/target CROSS_COMPILE= " /home/.../output/host/bin/csky-abiv2-linux- " DEPMOD=/home/.../output/host/sbin/depmod INSTALL_MOD_STRIP= 1 -C /home/.../linux uImage

如果是vmlinux,在結尾就是vmlinux。 

3.2 對Kernel進行配置

通過make linux-menuconfig可以對內核內部細節進行配置。

讓Linux內核帶符號表:

# CONFIG_COMPILE_TEST is not set

CONFIG_DEBUG_INFO=y

對目標板文件系統內容進行配置主要通過make menuconfig進入Target packages進行。

在Filesystem images中配置文件系統採用的格式,以及是否使用RAM fs。

4.1 ramfs

如果選中“initial RAM filesystem linked into linux kernel”,那麼文件系統會集成到vmlinux中。

如不選中,則vmlinux中只包括內核,文件系統會以其他形似提供,比如rootfs.cpio。

如果定義了BR2_TARGET_ROOTFS_INITRAMFS,那麼在編譯的末期需要重新編譯內核,將rootfs.cpio加入到vmlinux中。

fs/initramfs/initramfs.mk中:

複製代碼

rootfs-initramfs: linux-rebuild-with- initramfs

rootfs -initramfs-show- depends:
    @echo rootfs - cpio

.PHONY: rootfs -initramfs rootfs-initramfs-show- depends

ifeq ($(BR2_TARGET_ROOTFS_INITRAMFS),y)
TARGETS_ROOTFS += rootfs- initramfs
endif

複製代碼

在linux/linux.mk中:

複製代碼

.PHONY: linux-rebuild-with- initramfs
linux -rebuild-with-initramfs: $(LINUX_DIR)/ .stamp_target_installed
linux -rebuild-with-initramfs: $(LINUX_DIR)/ .stamp_images_installed
linux -rebuild-with-initramfs: rootfs- cpio
linux -rebuild-with- initramfs:
    @$(call MESSAGE, " Rebuilding kernel with initramfs " )
    # Build the kernel.
    $(LINUX_MAKE_ENV) $(MAKE) $(LINUX_MAKE_FLAGS) - C $(LINUX_DIR) $(LINUX_TARGET_NAME)
    $(LINUX_APPEND_DTB)
    # Copy the kernel image(s) to its(their) final destination
    $(call LINUX_INSTALL_IMAGE,$(BINARIES_DIR))
    # If there is a .ub file copy it to the final destination
    test ! -f $(LINUX_IMAGE_PATH).ub || cp $(LINUX_IMAGE_PATH).ub $(BINARIES_DIR)

複製代碼

在打開initramfs的情況下,重新將rootfs.cpio編譯進內核vmlinxu中。

然後將uImage之類的文件拷貝到BINARIES_DIR中。

要添加自己的本地APP, 首先在package/Config.in中添加指向新增APP目錄的Config.in;

然後在package中新增目錄helloworld,並在裡面添加Config.in和helloworld.mk;

最後添加對應的helloworld目錄。

5.1 添加package/Config.in入口

系統在make menuconfig的時候就可以找到對應的APP的Config.in。

複製代碼

diff --git a/package/Config. in b/package/Config. in 
index 43d75a9..6ef9fad 100644 
--- a/package/Config. in 
+++ b/package/Config. in 
@@ - 1868 , 5 + 1868 , 8 @@ menu " Text editors and viewers " 
        source " package/uemacs/Config.in " 
        source " package/vim/Config.in "
 endmenu
+menu " Private package " 
+ source " package/helloworld/Config.in " 
+endmenu

複製代碼

 如果在make menuconfig的時候選中helloworld,在make savedefconfig的時候就會打開BR2_PACKAGE_HELLOWORLD=y

5.2 配置APP對應的Config.in和mk文件

helloworld/Config.in文件,通過make menuconfig可以對helloworld進行選擇。

只有在BR2_PACKAGE_HELLOWORLD=y條件下,才會調用helloworld.mk進行編譯。

config BR2_PACKAGE_ HELLOWORLD
     bool  " helloworld "
    help
      This is a demo to add local app.

buildroot編譯helloworld所需要的設置helloworld.mk,包括源碼位置、安裝目錄、權限設置等。

下面的HELLOWORLD的開頭也是必須的。

複製代碼

################################################## ##############################
#
# helloworld
#
################################################## ##############################

HELLOWORLD _VERSION: = 1.0 . 0 
HELLOWORLD _SITE: = $(CURDIR)/work/helloworld
 HELLOWORLD _SITE_METHOD: = local
 HELLOWORLD _INSTALL_TARGET: = YES

define HELLOWORLD _BUILD_CMDS
    $(MAKE) CC = " $(TARGET_CC) " LD= " $(TARGET_LD) " - C $(@D) all
endef

define HELLOWORLD _INSTALL_TARGET_CMDS
    $(INSTALL) -D -m 0755 $(@D)/helloworld $(TARGET_DIR)/ bin
endef

define HELLOWORLD _PERMISSIONS
     /bin/helloworld f 4755  0  0 - - - - -
endef

$(eval $(generic -package))

複製代碼

如果源碼在git上,需要如下設置:

DMA_TEST_VERSION:= master--------------------------------------倉庫分支名稱
 DMA_TEST_SITE: =http: // .../dma.git-----------------------------倉庫git地址
 DMA_TEST_SITE_METHOD:=git----- --------------------------------獲取源碼的方式

_VERSION結尾的變量是源碼的版本號;_SITE_METHOD結尾的變量是源碼下載方法;_SITE結尾變量是源碼下載地址。

_BUILD_CMDS結尾的變量會在buildroot框架編譯的時候執行,用於給源碼的Makefile傳遞編譯選項和鏈接選項,調用源碼的Makefile。

_INSTALL_TARGET_CMDS結尾的變量是在編譯完之後,自動安裝執行,一般是讓buildroot把編譯出來的的bin或lib拷貝到指定目錄。

$(eval$(generic-package))最核心的就是這個東西了,一定不能夠漏了,不然源碼不會被編譯,這個函數就是把整個.mk構建腳本,通過Buildroot框架的方式,展開到Buildroot /目錄下的Makfile中,生成的構建目標(構建目標是什麼,還記得Makefile中的定義嗎?)。

5.3 編寫APP源碼

簡單的編寫一個helloworld.c文件:

複製代碼

#include <stdio.h>

void main( void )
{
    printf( " Hello world.\n " );
}

複製代碼

然後編寫Makefile文件:

複製代碼

CPPFLAGS +=  
LDLIBS += 

all: helloworld

analyzestack: helloworld.o
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)

clean:
    rm -f * .o helloworld

.PHONY: all clean

複製代碼

5.4 通過make menuconfig選中APP

通過Target packages -> Private package進入,選中helloworld。

然後make savedefconfig,對helloworld的配置就會保存到qemu_arm_vexpress_defconfig中。

5.5 編譯APP

可以和整個平台一起編譯APP;或者make helloworld單獨編譯。

這兩個文件在選中此APP之後,都會被拷貝到output/build/helloworld-1.0.0文件夾中。

然後生成的bin文件拷貝到output/target/bin/helloworld,這個文件會打包到文件系統中。

如果需要清空相應的源文件,通過make helloworld-dirclean。

5.6 運行APP

在shell中輸入helloworld,可以得到如下結果。

添加APP工作完成。

使用uboot作為bootloader,需要進行一些配置。

在選中U-boot作為bootloader之後,會彈出一系列相關配置。

“U-Boot board name”配置configs的defconfig名稱。

“U-Boot Version”選擇Custom Git repository,然後在“URL of custom repository”中選擇自己的git地址,並在“Custom repository version”中選擇git的分支。

在“U-Boot binary format”中選擇想要輸出的image格式,比如u-boot.img或者u-image.bin。

還可以選擇“Intall U-Boot SPL binary image”,選擇合適的SPL。

在buildroot編譯的末期,需要對編譯結果進行一些檢查或者其他操作。

buildroot預留了兩個接口:

BR2_ROOTFS_OVERLAY - 指向一個目錄,此目錄下的所有文件將會覆蓋到output/target下。比如一些配置文件,或者預編譯的庫等可以在此階段處理。

BR2_ROOTFS_POST_BUILD_SCRIPT - 一個腳本,更加複雜的對文件進行刪除、重命名、strip等等功能。

BR2_ROOTFS_POST_IMAGE_SCRIPT - 對最終生成的images進行打包處理等。

7.1 FS Overlay

有些應用或者配置不通過編譯,直接採取拷貝的方式集成到rootfs中,可以設置“System configuration”->“Root filesystem overlay directories”。

設置的目錄中的內容,會對output/target進行覆蓋。

相關處理在Makefile中如下:

    @$( foreach d, $(call qstrip,$(BR2_ROOTFS_OVERLAY)), \
        $(call MESSAGE, " Copying overlay $(d) " ); \
        rsync -a --ignore-times --keep- dirlinks $(RSYNC_VCS_EXCLUSIONS) \
             --chmod=u=rwX,go=rX --exclude .empty --exclude ' *~ ' \
            $(d) / $(TARGET_DIR)$(sep))

7.2 post build

除了fs overlay這種方式,buildroot還提供了一個腳本進行更加複雜的處理。

可以進行文件刪除、重命名,甚至對帶調試信息的文件進行strip等。

    @$( foreach s, $(call qstrip,$(BR2_ROOTFS_POST_BUILD_SCRIPT)), \
        $(call MESSAGE, " Executing post-build script $(s) " ); \
        $(EXTRA_ENV) $(s) $(TARGET_DIR) $(call qstrip,$(BR2_ROOTFS_POST_SCRIPT_ARGS))$(sep))

一個post_build.sh範例,對一系列文件進行刪除和strip操作:

複製代碼

#!/bin/ sh
# set - x
 set + o errexit

cp -a ${BINARIES_DIR}/deepeye1000e_hk.dtb ${BINARIES_DIR}/ deepeye1000.dtb

#Strip files in tbc_lists.txt. tbc means ' to be stripped ' .
STRIP =${HOST_DIR}/bin/csky-abiv2-linux- strip

for file in `cat ${BR2_EXTERNAL_INTELLIF_PATH}/board/deepeye1000e_hk/ tbs_lists.txt`
 do 
    if [ - e ${TARGET_DIR}${file} ]; then
        echo Strip ${file}.
        ${STRIP} ${TARGET_DIR}${file}
    else
        echo Not found ${file}.
    fi
done

#Delete files in tbd_lists.txt. tbd means ' to be deleted ' 
for file in `cat ${BR2_EXTERNAL_INTELLIF_PATH}/board/deepeye1000e_hk/ tbd_lists.txt`
 do 
    if [ - e ${TARGET_DIR}${file} ]; then
        echo Delete ${file}.
        rm ${TARGET_DIR}${file}
    else
        echo Not found ${file}.
    fi
done

${BR2_EXTERNAL_INTELLIF_PATH} /board/common/post_build.sh

複製代碼

7.2 post image

post image在post build之後,更傾向於生成完整的release文件。包括進行一些images打包、debug文件打包等等。

.PHONY: target-post- image
target -post-image: $(TARGETS_ROOTFS) target- finalize
    @$( foreach s, $(call qstrip,$(BR2_ROOTFS_POST_IMAGE_SCRIPT)), \
        $(call MESSAGE, " Executing post-image script $(s) " ); \
        $(EXTRA_ENV) $(s) $(BINARIES_DIR) $(call qstrip,$(BR2_ROOTFS_POST_SCRIPT_ARGS))$(sep))

一個範例如下,對images文件進行打包操作。

複製代碼

#!/bin/ sh
 set -x - e

IMG_DIR =output/ images
DEBUG_DIR =${IMG_DIR}/ debug
KERNEL_DIR =output/build/linux- master

ROOTFS_CPIO =${IMG_DIR}/ rootfs.cpio
KERNEL_IMAGE =${IMG_DIR}/ uImage
 SPL_IMAGE =${IMG_DIR}/u-boot-spl- bh.bin
UBOOT_IMAGE =${IMG_DIR}/u- boot.bin

IMG_TAR = images.tar.gz
DEBUG_TAR = debug.tar.gz
IMG_MD5 = images.md5

rm - f ${IMG_TAR} ${DEBUG_TAR} ${IMG_MD5}

mkdir - p ${DEBUG_DIR}
cp -a ${KERNEL_DIR}/vmlinux ${KERNEL_DIR}/System.map ${ROOTFS_CPIO} ${DEBUG_DIR}/

tar - czf ${IMG_TAR} ${KERNEL_IMAGE} ${SPL_IMAGE} ${UBOOT_IMAGE}
tar -czf ${DEBUG_TAR} -C ${IMG_DIR} debug/

md5sum ${IMG_TAR} > ${IMG_MD5}

複製代碼

buildroot還提供了一些命令,用於分析buildroot編譯過程中耗時、依賴關係、文件系統尺寸等等。

通過make help發現相關命令:

複製代碼

Documentation:
  manual                  - build manual in all formats
  manual -html - build manual in HTML
  manual -split-html - build manual in split HTML
  manual -pdf - build manual in PDF
  manual -text - build manual in text
  manual -epub - build manual in ePub
  graph -build - generate graphs of the build times
  graph -depends - generate graph of the dependency tree
  graph -size - generate stats of the filesystem size
  list -defconfigs - list all defconfigs (pre-configured minimal systems)

複製代碼

8.1 編譯耗時

執行make graph-build會生成如下文件:

其中比較有參考意義的文件是build.hist-duration.pdf文件,按照耗時從大到小排列。

通過此圖可以明白整個編譯流程時間都耗在哪裡,針對性進行分析優化,有利於提高編譯效率。

8.2 編譯依賴關係

生成graph-depends.pdf,可以看出各個編譯模塊之間的依賴關係。

buildroot的庫會根據依賴關係被自動下載,通過此圖也可以了解某些某塊被誰依賴。

8.3 編譯結果尺寸分析

通過graph-size.pdf文件可以對整個編譯結果組成有個大概理解。

另外更有參考意義的是file-size-stats.csv和package-size-stats.csv文件。

通過file和package兩個視角,更加詳細的了解整個rootfs空間都被那些文件佔用。

buildroot認知 

資料來源: https://www.cnblogs.com/arnoldlu/p/9553995.html

沒有留言: