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 options | arch/Config.in | |
Build options | Config.in | |
Toolchain | toolchain/Config.in | |
System configuration | system/Config.in | |
Kernel | linux/Config.in | |
Target packages | package/Config.in | |
Target packages->Busybox | ||
Filesystem images | fs/Config.in | |
Bootloaders | boot/Config.in | |
Host utilities | package/Config.in.host | |
Legacy config options | Config.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空間都被那些文件佔用。
資料來源: https://www.cnblogs.com/arnoldlu/p/9553995.html
沒有留言:
張貼留言