Buildroot是一种工具,它使用交叉编译简化并自动化为嵌入式系统构建完整的Linux系统
第1章 关于Buildroot
为了实现这一点,Buildroot能够为您的目标生成交叉编译工具链、根文件系统、Linux内核映像和引导加载程序。Buildroot可以单独用于这些选项的任何组合(例如,您可以使用现有的交叉编译工具链,并用Buildroot只构建您的根文件系统)。
Buildroot主要对使用嵌入式系统的人有用。嵌入式系统通常使用的处理器不是常规x86处理器。它们可以是PowerPC处理器、MIPS处理器、ARM处理器等等。
Buildroot支持许多处理器和他们的变种;它还提供了一些现成的板的默认配置。除此之外,许多第三方项目都基于或在Buildroot之上开发他们的BSP或SDK。
BSP:板级支持包
SDK:软件开发包
第二章 系统需求
Buildroot是为Linux系统而设计的
虽然Buildroot本身将构建编译所需的大多数主机包,但某些标准的Linux实用程序预计已经安装在主机系统上。下面是强制包和可选包的概述(注意包的名称可能在不同的发行版中有所不同)。
2.1 强制性的包
- 构建工具
which
sed
make
(version 3.81 or any later)binutils
build-essential
(only for Debian based systems)gcc
(version 4.8 or any later)g++
(version 4.8 or any later)bash
patch
gzip
bzip2
perl
(version 5.8.7 or any later)tar
cpio
unzip
rsync
file
(must be in/usr/bin/file
)bc
- 下载工具
wget
第三章 获取Buildroot
Buildroot每3个月发布一次,分别在2月、5月、8月和11月。发布号格式为YYYY.MM,比如2013.02,2014.08。
发布tar包可以在http://buildroot.org/downloads/上找到。
为了方便,在Buildroot源码目录树的support/misc/Vagrantfile
中有一个Vagrantfile,用于快速设置一个具有所需依赖项的虚拟机。
如果你想在Linux或Mac Os X上设置一个独立的内置环境,请将这一行粘贴到你的终端:
curl -O https://buildroot.org/downloads/Vagrantfile; vagrant up
第四章 Buildroot快速启动
使用Buildroot的第一步是创建配置。Buildroot有一个很好的配置工具,类似于Linux内核或BusyBox中的配置工具。
从buildroot目录中运行
$ make menuconfig
对于原始的基于curses的配置器,可以运行:
$ make nconfig
对于新的基于curses的配置器,可以运行:
$ make xconfig
所有这些“make”命令都需要构建一个配置实用程序(包括接口),因此您可能需要为配置实用程序使用的相关库安装“development”包。请参阅第2章系统需求,以获得更多细节,特别是可选需求,以获得您喜爱的接口的依赖关系。
有关某些特定配置方面的详细信息,请参阅第6章Buildroot配置
一旦配置完毕,配置工具将生成一个包含整个配置的.config文件。这个文件将被顶级的Makefile读取。 要启动构建过程,只需运行:
$ make
默认情况下,Buildroot不支持顶级并行构建,因此没有必要运行make -jN。然而,对于顶级并行构建有实验性的支持,请参阅8.11节“顶级并行构建”。
make命令通常执行以下步骤:
- 下载源文件(如有需要)
- 配置,构建并安装交叉编译工具链,或者简单地导入外部工具链;
- 配置,构建和安装选定的目标包;
- 如果选中,则构建一个内核映像;
- 如果选中,构建一个引导加载程序映像;
- 以选定的格式创建根文件系统。
Buildroot输出存储在单个目录output/
中。这个目录包含几个子目录:
images/
:所有映像(内核映像、引导加载程序映像和根文件系统映像)都存储在其中。这些是您需要放在目标系统上的文件。build/
:在其中构建所有组件(包括宿主上的Buildroot需要的工具和为目标编译的包)。此目录为每个组件包含一个子目录。host/
:包含为主机构建的工具和目标工具链的sysroot。前者安装了为主机编译的工具,这些工具是正确执行Buildroot所需要的,包括交叉编译工具链。后者是类似于根文件系统层次结构的层次结构。它包含提供和安装其他包使用的库的所有用户空间包的头文件和库文件。但是,这个目录并不打算成为目标的根文件系统:它包含许多开发文件、未剥离的二进制文件和库,使得它对于嵌入式系统来说太大了。这些开发文件用于为依赖于其他库的目标编译库和应用程序。staging/
: 是指向host/
内部目标工具链sysroot的符号链接,存在它是为了向后兼容。target/
:它包含了目标的几乎完整的根文件系统:除了/dev/
中的设备文件之外,所需的所有文件都在其中(Buildroot不能创建它们,因为Buildroot不是作为根运行,也不想作为根运行)。而且,它没有正确的权限(例如busybox二进制文件的setuid)。因此,不应该在目标上使用此目录。相反,您应该使用在images/
目录中构建的映像之一。如果需要在NFS上引导根文件系统的一个提取的映像,那么使用在images/
中生成的tarball映像并将其提取为根。与staging/
相比,target/
只包含运行所选目标应用程序所需的文件和库:开发文件(头文件等)不存在,二进制文件被剥离。
用户指南
第六章 Buildroot配置
make *config
中的所有配置选项都有一个帮助文本,提供有关该选项的详细信息。
make *config
命令还提供了一个搜索工具。阅读不同前端菜单中的帮助信息,了解如何使用它:
- 在menuconfig中,通过按
/
调用搜索工具
搜索结果显示匹配项的帮助信息。在menuconfig中,左列中的数字为相应的条目提供了快捷方式。只要键入这个数字直接跳到条目,或者跳到包含的菜单,以防条目由于缺少依赖项而不可选择。
6.1 交叉编译工具链
编译工具链是允许您为系统编译代码的一组工具。它由一个编译器(在我们的例子中是gcc)、像汇编器和链接器这样的二进制工具(在我们的例子中是binutils)和一个C标准库(例如GNU Libc, uClibc-ng)组成。
安装在您的开发站上的系统当然已经有一个编译工具链,您可以使用它来编译在您的系统上运行的应用程序。如果使用PC,则编译工具链运行在x86处理器上,并为x86处理器生成代码。在大多数Linux系统下,编译工具链使用GNU libc (glibc)作为C标准库。这个编译工具链称为“主机编译工具链”。运行它的机器和您工作的机器称为“主机系统”[3]。
编译工具链是由您的发行版提供的,而Buildroot与之无关(除了使用它构建交叉编译工具链和运行在开发主机上的其他工具之外)。
如上所述,系统附带的编译工具链在主机系统的处理器上运行并为其生成代码。由于嵌入式系统有不同的处理器,所以需要一个交叉编译工具链——一个运行在主机系统上但为目标系统(和目标处理器)生成代码的编译工具链。例如,如果您的主机系统使用x86,而您的目标系统使用ARM,那么您主机上的常规编译工具链运行在x86上并为x86生成代码,而交叉编译工具链运行在x86上并为ARM生成代码。
Buildroot为交叉编译工具链提供了两种解决方案:
- 内部工具链后端,在配置接口中称为Buildroot工具链。
- 外部工具链后端,在配置接口中称为外部工具链。
使用工具链菜单中的工具链类型选项来选择这两种解决方案。一旦选择了一个解决方案,就会出现许多配置选项,下面几节将详细介绍这些选项。
6.1.1. 内部工具链后端
内部工具链后端是Buildroot在为目标嵌入式系统构建用户空间应用程序和库之前,自行构建交叉编译工具链的后端。
这个后端支持几个C库: uClibc-ng、glibc和musl
一旦选择了这个后端,就会出现许多选项。最重要的是:
- 更改用于构建工具链的Linux内核头的版本。这一项值得做一些解释。在构建交叉编译工具链的过程中,正在构建C库。这个库提供了用户空间应用程序和Linux内核之间的接口。为了知道如何与Linux内核“对话”,C库需要访问Linux内核头文件(即来自内核的.h文件),它定义了用户空间与内核之间的接口(系统调用、数据结构等)。由于此接口是向后兼容的(较高版本的程序能顺利处理较低版本程序的数据),因此用于构建工具链的Linux内核头文件的版本不需要与您打算在嵌入式系统上运行的Linux内核的版本完全匹配。它们只需要有一个与您要运行的Linux内核版本相同或更老的版本。如果您使用的内核头文件比您在嵌入式系统上运行的Linux内核最新,那么C库可能使用的接口不是您的Linux内核提供的。
- 更改GCC编译器、binutils和C库的版本
- 选择一些工具链选项(仅限uClibc):工具链是否应该具有RPC支持(主要用于NFS)、宽字符支持、语言环境支持(用于国际化)、c++支持或线程支持。根据您选择的选项,Buildroot菜单中可见的用户空间应用程序和库的数量将会改变:许多应用程序和库需要启用某些工具链选项。当需要某个工具链选项来启用那些包时,大多数包都会显示注释。如果需要,可以通过运行
make uclibc-menuconfig
进一步优化uClibc配置。但是请注意,Buildroot中的所有包都是根据内置的默认uClibc配置进行测试的:如果您通过删除uClibc的特性而偏离了这个配置,那么一些包可能不再构建。
值得注意的是,每当修改其中一个选项时,就必须重新构建整个工具链和系统。参见第8.2节“了解何时需要进行完全重建”。
这个后端优势:
- 与Buildroot很好的集成
- 快速,只构建必要的内容
这个后端的缺点:
- 清理时需要重新构建工具链,这需要时间。如果您试图减少构建时间,可以考虑使用外部工具链后端。
6.1.2 外部工具链后端
外部工具链后端允许使用现有的预构建的交叉编译工具链。Buildroot知道许多著名的交叉编译工具链(来自用于ARM的Linaro、用于ARM的Sourcery CodeBench、x86-64、PowerPC和MIPS),并且能够自动下载它们,或者指向一个定制的工具链,可以下载,也可以在本地安装。
然后,你有三种使用外部工具链的解决方案:
- 使用预定义的外部工具链配置文件,并让Buildroot下载、提取和安装工具链。Buildroot已经知道一些CodeSourcery和Linaro工具链。从可用的工具链中选择工具链配置文件。这绝对是最简单的解决方案。
- 使用预定义的外部工具链配置文件,但不是让Buildroot下载和提取工具链,您可以告诉Buildroot您的工具链已经安装在系统的哪个位置。只需通过可用的工具链选择工具链配置文件,自动取消选择下载工具链,并用交叉编译工具链的路径填充工具链路径文本条目。
- 使用完全自定义的外部工具链。这对于使用crosstool-NG或使用Buildroot本身生成的工具链特别有用。为此,在工具链列表中选择自定义工具链(Custom toolchain)解决方案。您需要填充工具链路径、工具链前缀和外部工具链C库选项。然后,您必须告诉Buildroot您的外部工具链支持什么。如果您的外部工具链使用glibc库,您只需要告知您的工具链是否支持c++以及它是否有内置的RPC支持。如果您的外部工具链使用uClibc库,那么您必须告诉Buildroot它是否支持RPC、宽字符、语言环境、程序调用、线程和c++。在执行的开始,Buildroot会告诉您所选选项是否与工具链配置不匹配。
我们的外部工具链支持已经通过来自CodeSourcery和Linaro的工具链、crosstool-NG生成的工具链以及Buildroot本身生成的工具链进行了测试。一般来说,所有支持sysroot特性的工具链都可以工作。如果没有,请与开发者联系。
我们不支持OpenEmbedded或Yocto生成的工具链或SDK,因为这些工具链不是纯粹的工具链(例如,只是编译器、binutils、C和c++库)。相反,这些工具链附带了一组非常大的预编译库和程序。因此,Buildroot不能导入工具链的sysroot,因为它将包含数百兆的预编译库,这些库通常是由Buildroot构建的。
我们也不支持使用发行版工具链(例如,你的发行版安装的gcc/binutils/C库)作为为目标构建软件的工具链。这是因为您的发布工具链不是“纯”的工具链(即只与C/C++库一起使用),所以我们不能正确地将其导入到Buildroot构建环境中。因此,即使是为x86或x86_64目标构建系统,也必须使用Buildroot或crosstool-NG生成交叉编译工具链。
如果您想为您的项目生成一个定制的工具链,它可以在Buildroot中用作外部工具链,我们建议使用Buildroot本身(参见6.1.3节“使用Buildroot构建外部工具链“)或者使用crosstool-NG来构建它。
这个后端优势:
- 允许使用众所周知且经过良好测试的交叉编译工具链。
- 避免交叉编译工具链的构建时间,这在嵌入式Linux系统的总体构建时间中通常非常重要。
这个后端的缺点:
- 如果预先构建的外部工具链有错误,可能很难从工具链供应商那里得到修复,除非您使用Buildroot或Crosstool-NG自己构建外部工具链。
6.1.3. 使用Buildroot构建外部工具链
内置的内部工具链选项可以用来创建外部工具链。下面是构建内部工具链并将其打包以供Buildroot自身(或其他项目)重用的一系列步骤。
创建一个新的Buildroot配置,具体如下:
- 为您的目标CPU架构选择适当的目标选项(Target options)
- 在工具链菜单中,为工具链类型保留默认的Buildroot工具链,并根据需要配置您的工具链
- 在系统配置菜单中,选择None作为Init系统,None作为/bin/sh
- 在目标包(Target packages)菜单中,禁用BusyBox
- 在“文件系统映像( Filesystem images)”菜单中,禁用“tar”根文件系统
然后,我们可以触发构建,并要求Buildroot生成SDK。这将方便地为我们生成一个包含工具链的tarball:
make sdk
这将在$(O)/images
中生成SDK tarball,名称类似于arm-buildroot-linux-uclibcgnueabi_sdk-buildroot.tar.gz
。保存这个tarball,因为它现在是一个工具链,您可以在其他Buildroot项目中重用它作为外部工具链。
在那些其他的Buildroot项目中,在工具链菜单:
- 将工具链类型设置为外部工具链
- 设置工具链为自定义工具链
- 将工具链源头设置为要下载和安装的工具链
- 设置工具链URL为
file:///path/to/your/sdk/tarball.tar.gz
外部工具链包装
当使用外部工具链时,Buildroot生成一个包装程序,它透明地将适当的选项(根据配置)传递给外部工具链程序。如果你需要调试这个包装器来检查到底传递了什么参数,你可以将环境变量BR2_DEBUG_WRAPPER
设置为以下任意一个:
- 0:为空或未设置:没有调试
- 1:用一行跟踪所有参数
- 2:每行跟踪一个参数
6.2. /dev管理
在Linux系统上,/dev
目录包含称为设备文件的特殊文件,这些文件允许用户空间应用程序访问由Linux内核管理的硬件设备。如果没有这些设备文件,您的用户空间应用程序将不能使用硬件设备,即使Linux内核正确地识别了它们。
在系统配置/dev管理(System configuration, /dev)下,Buildroot提供了四种不同的解决方案来处理/dev
目录:
- 第一个解决方案是静态使用设备表。这是Linux中处理设备文件的传统方法。使用这种方法,设备文件会持久地存储在根文件系统中(也就是说,它们会在重新引导时持久保存),并且当硬件设备从系统中添加或删除时,不会自动创建和删除这些设备文件。因此,Buildroot使用设备表创建一组标准的设备文件,默认的设备表存储在Buildroot源代码中的
system/device_table_dev.txt
中。这个文件在Buildroot生成最终根文件系统映像时被处理,因此设备文件在output/target
目录中不可见。BR2_ROOTFS_STATIC_DEVICE_TABLE
选项允许更改Buildroot使用的默认设备表,或者添加额外的设备表,以便Buildroot在构建期间创建额外的设备文件。所以,如果你使用这个方法,设备文件丢失在你的系统中,可以创建一个board/<your company>/<your project>/device_table_dev.txt
文件,其中包含您的附加设备的描述文件,然后您可以设置BR2_ROOTFS_STATIC_DEVICE_TABLE
到system/device_table_dev.txt
、board/<your company>/<your project>/device_table_dev.txt
。有关设备表文件格式的更多细节,请参阅第24章Makedev语法文档。 - 第二种解决方案是动态的,只使用devtmpfs。devtmpfs是在内核2.6.32中引入的Linux内核内的一个虚拟文件系统(如果使用较旧的内核,则不可能使用此选项)。当挂载到/dev中时,这个虚拟文件系统将自动使设备文件随着硬件设备的添加和从系统中删除而出现和消失。这个文件系统在重新引导时不是持久的:它是由内核动态填充的。使用devtmpfs需要启用以下内核配置选项:
CONFIG_DEVTMPFS
和CONFIG_DEVTMPFS_MOUNT
。当Buildroot负责为嵌入式设备构建Linux内核时,它会确保启用这两个选项。但是,如果您在Buildroot之外构建Linux内核,那么启用这两个选项是您的责任(如果不能这样做,您的Buildroot系统将无法引导)。 - 第三种解决方案是动态使用devtmpfs + mdev。该方法还依赖于上面详细介绍的devtmpfs虚拟文件系统(因此在内核配置中启用
CONFIG_DEVTMPFS
和CONFIG_DEVTMPFS_MOUNT
的要求仍然适用),但是在其上添加了mdev用户空间实用程序。mdev是BusyBox的一个程序部分,内核将在每次添加或删除设备时调用它。由于/etc/mdev.conf
配置文件,mdev可以配置为,例如,设置设备文件的特定权限或所有权,在设备出现或消失时调用脚本或应用程序,等等。基本上,它允许用户空间对设备添加和删除事件作出反应。例如,mdev可用于在系统上出现设备时自动加载内核模块。如果您的设备需要固件,mdev也很重要,因为它将负责将固件内容推送到内核。mdev是udev的轻量级实现(具有更少的特性)。有关mdev及其配置文件语法的详细信息,请参阅http://git.busybox.net/busybox/tree/docs/mdev.txt。 - 第四种解决方案是动态使用devtmpfs + eudev。这种方法也依赖于上面详细介绍的devtmpfs虚拟文件系统,但是在其上添加了eudev用户空间守护进程。eudev是一个后台运行的守护进程,当设备从系统中添加或删除时,内核将调用它。它是比mdev更重量级的解决方案,但是提供了更高的灵活性。eudev是udev的一个独立版本,udev是大多数桌面Linux发行版中使用的原始用户空间守护进程,现在是Systemd的一部分。更多细节,请参见http://en.wikipedia.org/wiki/Udev。
Buildroot开发人员的建议是从动态仅使用devtmpfs解决方案开始,除非你需要在添加/删除设备或需要固件时通知用户空间,在这种情况下动态使用devtmpfs + mdev通常是一个很好的解决方案。
注意,如果选择systemd作为init系统,/dev
管理器(management)将由systemd提供的udev程序执行。
6.3. 初始化系统
init程序是内核启动的第一个用户空间程序(PID值为1),它负责启动用户空间服务和程序(例如:web服务器、图形化应用程序、其他网络服务器等)。
内置droot允许使用三种不同类型的init系统,可以从系统配置中选择,init系统:
- 第一个解决方案是BusyBox。在许多程序中,BusyBox有一个基本的init程序的实现,这对于大多数嵌入式系统来说已经足够了。启用
BR2_INIT_BUSYBOX
将确保BusyBox将构建并安装它的init程序。这是Buildroot的默认解决方案。BusyBox init程序将在引导时读取/etc/inittab
文件,以知道要做什么。这个文件的语法可以在http://git.busybox.net/busybox/tree/examples/inittab中找到(注意,BusyBox的inittab语法是特殊的:不要使用来自Internet的随机的inittab文档来了解BusyBox inittab)。默认的inittab存储在system/skeleton/etc/inittab
中。除了挂载一些重要的文件系统外,默认inittab的主要工作是启动/etc/init.d/rcS
shell脚本,并启动getty
程序(它提供登录提示符)。 - 第二种方法是systemV。这个解决方案使用旧的传统sysvinit程序,内置在
package/sysvinit
中。这是大多数桌面Linux发行版中使用的解决方案,直到它们切换到更近期的替代方案,如Upstart或Systemd。sysvinit还可以与inittab文件一起工作(它的语法与BusyBox中的语法略有不同)。这个init解决方案安装的默认inittab位于package/sysvinit/inittab
中。 - 第三种方法是systemd。systemd是Linux的新一代init系统。它远远超过传统的init程序:积极的并行处理能力,使用套接字和
D-Bus activation
开始服务,提供按需启动守护进程,跟踪过程使用Linux对照组,支持系统的快照和恢复状态,等systemd将在相对复杂的嵌入式系统是有用的,例如要求的D-Bus
和服务彼此之间的交流。值得注意的是,systemd带来了大量的大依赖:dbus、udev等等。有关systemd的更多细节,请参见http://www.freedesktop.org/wiki/Software/systemd。
Buildroot开发人员推荐的解决方案是使用BusyBox init,因为它对于大多数嵌入式系统已经足够了。systemd可以用于更复杂的情况。
第七章 其他组件配置
在尝试修改下面的任何组件之前,请确保已经配置了Buildroot本身,并启用了相应的包。
- BusyBox
- 如果您已经有一个BusyBox配置文件,那么您可以使用
BR2_PACKAGE_BUSYBOX_CONFIG
在Buildroot配置中直接指定这个文件。否则,Buildroot将从默认的BusyBox配置文件开始。 - 要对配置进行后续更改,请使用
make busybox-menuconfig
打开BusyBox配置编辑器。 - 也可以通过环境变量指定BusyBox配置文件,但不建议这样做。请参阅8.6节“环境变量”以了解更多细节。
- 如果您已经有一个BusyBox配置文件,那么您可以使用
- uClibc
- 配置uClibc的方式与BusyBox相同。指定现有配置文件的配置变量是
BR2_UCLIBC_CONFIG
。进行后续更改的命令是make uclibc-menuconfig
。
- 配置uClibc的方式与BusyBox相同。指定现有配置文件的配置变量是
- Linux kernel
- 如果已经有内核配置文件,可以使用
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG
在Buildroot配置中直接指定该文件。 - 如果您还没有内核配置文件,您可以首先在Buildroot配置中使用
BR2_LINUX_KERNEL_USE_DEFCONFIG
指定defconfig,或者首先创建一个空文件并使用BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG
将其指定为定制配置文件。 - 要对配置进行后续更改,请使用
make linux-menuconfig
打开Linux配置编辑器。
- 如果已经有内核配置文件,可以使用
- Barebox
- 对Barebox的配置与Linux内核的配置方式相同。相应的配置变量是
BR2_TARGET_BAREBOX_USE_CUSTOM_CONFIG
和BR2_TARGET_BAREBOX_USE_DEFCONFIG
。要打开配置编辑器,请使用make barebox-menuconfi
g。
- 对Barebox的配置与Linux内核的配置方式相同。相应的配置变量是
- U-Boot
- 配置U-Boot(版本为2015.04或更新)的方式与Linux内核相同。相应的配置变量是
BR2_TARGET_UBOOT_USE_CUSTOM_CONFIG
和BR2_TARGET_UBOOT_USE_DEFCONFIG
。要打开配置编辑器,请使用make uboot-menuconfig
。
- 配置U-Boot(版本为2015.04或更新)的方式与Linux内核相同。相应的配置变量是
第八章 一般Buildroot用法
8.1. 使用技巧
这是一个技巧的收集,帮助你充分利用Buildroot。
-
显示make执行的所有命令:
$ make V=1 <target>
-
使用defconfig显示板列表:
$ make list-defconfigs
-
显示所有可用目标:
$ make help
不是所有的目标都是可用的,
.config
文件中的一些设置可能会隐藏一些目标:busybox-menuconfig
只在启用busybox时工作;linux-menuconfig
和linux-savedefconfig
只在启用linux时工作;uclibc -menuconfig
仅在内部工具链后端选择uClibc C库时可用barebox-menuconfig
和barebox-savedefconfig
只在barebox引导加载程序启用时工作uboot-menuconfig
和uboot-savedefconfig
只在U-Boot引导加载程序启用时工作
-
Cleaning:当任何架构或工具链配置选项被更改时,需要显式清理。
-
删除所有构建产品(包括构建目录、主机、分段和目标树、图像和工具链):
$ make clean
-
-
生成手册:当前的手册源位于docs/manual目录中。要生成手册:
$ make manual-clean $ make manual
输出的手册在
output/docs/manual
中 -
为新目标重置Buildroot:删除所有构建产品以及配置:
$ make distclean
-
转储内部的make变量
-
转储已知的变量,以及它们的值
$ make -s printvars VARS='VARIABLE1 VARIABLE2' VARIABLE1=value_of_variable
-
使用一些变量可以调整输出
-
VARS将把列表限制为名称与指定make模式匹配的变量—这必须设置,否则不打印任何内容
$ make -s printvars VARS=BUSYBOX_%DEPENDENCIES BUSYBOX_DEPENDENCIES=skeleton toolchain BUSYBOX_FINAL_ALL_DEPENDENCIES=skeleton toolchain
-
如果设置为YES, QUOTED_VARS将单引号引用该值
$ make -s printvars VARS=BUSYBOX_%DEPENDENCIES QUOTED_VARS=YES BUSYBOX_DEPENDENCIES='skeleton toolchain' BUSYBOX_FINAL_ALL_DEPENDENCIES='skeleton toolchain'
-
如果将RAW_VARS设置为YES,将打印未展开的值
$ make -s printvars VARS=BUSYBOX_%DEPENDENCIES RAW_VARS=YES BUSYBOX_DEPENDENCIES=skeleton toolchain BUSYBOX_FINAL_DEPENDENCIES=$(sort $(BUSYBOX_DEPENDENCIES)
-
输出的引号变量可以在shell脚本中重用,例如:
$ eval $(make -s printvars VARS=BUSYBOX_DEPENDENCIES QUOTED_VARS=YES) $ echo $BUSYBOX_DEPENDENCIES skeleton toolchain
-
-
8.2. 理解何时有必要进行全面重建
当通过make menuconfig
、make xconfig
或其他配置工具更改系统配置时,Buildroot不会尝试检测应该重新构建系统的哪些部分。在某些情况下,Buildroot应该重建整个系统,在某些情况下,只重建包的特定子集。但是以完全可靠的方式检测它是非常困难的,因此Buildroot开发人员决定不尝试这样做。
相反,用户有责任知道何时需要完全重新构建。作为一个提示,这里有一些经验规则,可以帮助你理解如何使用Buildroot:
- 当目标体系结构配置更改时,需要完全重新构建。改变架构变体,例如二进制格式或浮点策略会对整个系统产生影响。
- 当工具链配置被更改时,通常需要一个完整的重新构建。更改工具链配置通常涉及更改编译器版本、C库或其配置的类型,或其他一些基本配置项,而这些更改会对整个系统产生影响。
- 在向配置中添加附加包时,不需要完全重新构建。Buildroot将检测到这个包从未构建过,并将构建它。但是,如果这个包是一个库,可以被已经构建的包选择性地使用,那么Buildroot将不会自动重新构建这些包。要么您知道应该重新构建哪些包,然后您可以手动重新构建它们,要么您应该进行完全的重新构建。例如,假设您使用
ctorrent
包构建了一个没有openssl
的系统。你的系统工作了,但是你意识到你想要在ctorrent中有SSL支持,所以你在Buildroot配置中启用openssl包并重新启动构建。Buildroot会检测到应该构建和将要构建openssl,但是它不会检测到应该重新构建ctorrent以受益于openssl以增加对openssl的支持。你将不得不做一个完整的重建,或重建ctorrent本身。 - 当从配置中删除包时,Buildroot不会做任何特殊的操作。它不会从目标根文件系统或工具链sysroot中删除此包安装的文件。要摆脱这个包,需要完全重新构建。但是,通常您并不需要立即删除这个包:您可以等到下一个午休时间再从头开始构建。
- 当更改包的子选项时,不会自动重新生成包。在进行了这些更改之后,通常只重新构建这个包就足够了,除非启用package子选项会向包中添加一些对已经构建的另一个包有用的特性。同样,Buildroot不跟踪什么时候应该重新构建一个包:一旦构建了一个包,就不会重新构建它,除非被显式地告知这样做。
- 当对根文件系统框架进行更改时,需要完全重新构建。但是,当对根文件系统的更改覆盖时,生成一个后构建脚本或后映像脚本时,不需要进行完全的重新构建:一个简单的make调用将考虑这些更改。
- 当重新生成或删除
FOO_DEPENDENCIES
中列出的包时,不会自动重新生成包foo。例如,如果在FOO_DEPENDENCIES
中使用FOO_DEPENDENCIES = bar
列出了一个包bar
,并且更改了bar
包的配置,那么配置更改不会导致自动重新生成包foo。在这种情况下,您可能需要重新构建构建中的依赖项中引用bar的任何包,或者执行完整的重新构建以确保任何依赖bar的包都是最新的。
一般来说,当您面临构建错误并且不确定您所做的配置更改的潜在后果时,请执行完全重构。如果您得到相同的构建错误,那么您可以确定该错误与包的部分重构无关,如果该错误发生在来自官方Buildroot的包中,请毫不犹豫地报告该问题!随着您使用Buildroot的经验的发展,您将逐步了解到什么时候真正需要进行完整的重建,并且您将节省越来越多的时间。
作为参考,完全重建是通过运行:
$ make clean all
8.3. 了解如何重新构建包
Buildroot用户最常见的问题之一是如何重新构建给定的包,或者如何在不从头重新构建所有内容的情况下删除包。
如果不从头重新构建,Buildroot不支持删除包。这是因为Buildroot不跟踪哪个包在输出/准备目录和输出/目标目录中安装了什么文件,或者根据另一个包的可用性不同,哪个包会进行不同的编译。
从头重新构建一个包的最简单方法是在output/build
中删除它的构建目录。然后Buildroot将重新提取、重新配置、重新编译和重新安装这个包。您可以通过make <package>-dirclean
命令要求buildroot完成此操作。
另一方面,如果只想从编译步骤重启包的构建过程,可以运行make <package>-rebuild
。它将重新启动包的编译和安装,但不是从头开始:它基本上会在包内重新执行make和make install,因此它只会重新构建更改过的文件。
如果希望从包的配置步骤重新启动包的构建过程,可以运行make <package>-reconfigure
。它将重新启动包的配置、编译和安装。
<package>-rebuild
表示<package>-reinstall
, <package>-reconfigure
表示<package>-rebuild
,这些目标以及<package>
只对上述软件包起作用,不触发重新创建根文件系统映像。如果需要重新创建根文件系统,则应该另外运行make或make all。
在内部,Buildroot创建所谓的戳记文件,以跟踪每个包的哪些构建步骤已经完成。它们存储在包构建目录中,output/build/<package>-<version>/
,并被命名为.stamp_<step-name>
。上面详细描述的命令只是操作这些戳记文件,以强制Buildroot重新启动包构建过程的特定步骤集。
关于包特殊制作目标的进一步细节在第8.13.5节“特定于包的制作目标”中有解释。
8.4. 离线构建
如果你打算做一个离线构建,只是想下载你之前在配置器(menuconfig, nconfig, xconfig或gconfig)中选择的所有资源,那么执行:
$ make source
现在可以断开连接或将dl
目录的内容复制到构建主机。
8.5. 构建到其他目录
默认情况下,Buildroot构建的所有内容都存储在Buildroot的输出目录中。
Buildroot还支持使用类似于Linux内核的语法从其他目录树中构建。在make命令行中添加O=<directory>
:
$ make O=/tmp/build
或者:
$ cd /tmp/build; make O=$PWD -C path/to/buildroot
所有输出文件都位于/tmp/build
之下。如果O路径不存在,Buildroot将创建它。
注意:O路径既可以是绝对路径,也可以是相对路径,但如果它作为相对路径传递,需要注意的是,它是相对于主Buildroot源目录解释的,而不是当前工作目录。
当使用外部目录树构建时,Buildroot的 .config
和临时文件也存储在输出目录中。这意味着您可以使用相同的源树安全地并行运行多个构建,只要它们使用唯一的输出目录。
为了方便使用,Buildroot在输出目录中生成一个Makefile包装器——所以在第一次运行后,您不再需要传递O=<…>
和- c <…>
,只需运行(在输出目录中):
$ make <target>
8.6. 环境变量
当环境变量传递给make或通过set设置是,Buildroot也会优先使用它们:
环境变量 | 说明 |
---|---|
HOSTCXX | C++编译器 |
HOSTCC | C 编译器 |
UCLIBC_CONFIG_FILE = <path/to/config> |
如果正在构建一个内部工具链,则指向用于编译uClibc的uClibc配置文件的路径。注意,uClibc配置文件也可以从配置接口设置,所以通过Buildroot的 .config 文件;这是推荐的设置方法。 |
BUSYBOX_CONFIG_FILE=<path/to/.config> |
BusyBox配置文件的路径。注意,BusyBox配置文件也可以从配置接口设置,因此通过Buildroot的 .config 文件;这是推荐的设置方法 |
使用位于toplevel目录和$HOME
中的配置文件的示例
$ make UCLIBC_CONFIG_FILE=uClibc.config BUSYBOX_CONFIG_FILE=$HOME/bb.config
如果希望使用默认gcc或g++以外的编译器在主机上构建helper二进制文件,那么就执行
$ make HOSTCXX=g++-4.3-HEAD HOSTCC=gcc-4.3-HEAD
8.7 有效地处理文件系统映像
文件系统映像可以变得很大,这取决于您选择的文件系统、包的数量、是否提供了空闲空间……然而,文件系统映像中的某些位置可能只是空的(例如,长时间运行的零);这样的文件称为稀疏文件。
大多数工具都可以有效地处理稀疏文件,并且只存储或编写稀疏文件中不为空的部分。
例如:
- tar接受-S选项,告诉它只存储稀疏文件的非零块:
tar cf archive.tar -S [files...]
将在一个tarball中有效地存储稀疏文件tar xf archive.tar -S
将有效地从tarball(压缩)文件中提取存储的稀疏文件
- cp接受-sparse=WHEN选项(WHEN是auto、never或always的选项之一):
cp --spares=always source.file dest.file
如果source.file有长时间运行的零,将使de st.file成为一个稀疏文件。
如果您需要存储文件系统映像(例如,将它们从一台机器传输到另一台机器),或者如果您需要发送它们(例如,发送到Q&A团队),您可以使用稀疏文件。
但是请注意,在使用dd的稀疏模式时,将文件系统映像传输到设备上可能会导致文件系统损坏(例如,ext2文件系统的块位图可能被损坏;或者,如果您的文件系统中有稀疏的文件,那么这些部分在回读时可能不全是零)。您应该只在处理构建机器上的文件时使用sparse文件,而不是在将它们传输到将在目标机上使用的实际设备时使用。
8.8 用图形表示包之间的依赖关系
Buildroot的工作之一是了解包之间的依赖关系,并确保以正确的顺序构建它们。这些依赖关系有时可能相当复杂,并且对于给定的系统,通常不容易理解为什么这样或那样的包被Buildroot带入构建中。
为了帮助理解依赖关系,从而更好地理解嵌入式Linux系统中不同组件的作用,Buildroot能够生成依赖关系图。
要生成已编译的整个系统的依赖图,只需运行:
make graph-depends
您将在output/graphs/graphics-dependent.pdf
中找到生成的图形。
如果您的系统相当大,依赖关系图可能太复杂和难以阅读。因此,可以为一个给定的包生成依赖图:
$ make <pkg>-graph-depends
您将在output/graph/<pkg>-graph-dependent.pdf
中找到生成的图形。
请注意,依赖关系图是使用Graphviz项目中的点工具生成的,要使用这个特性,必须在系统上安装这个工具。在大多数发行版中,可以使用graphviz包。
默认情况下,依赖关系图以PDF格式生成。但是,通过传递BR2_GRAPH_OUT
环境变量,您可以切换到其他输出格式,如PNG、PostScript或SVG。支持dot工具的-T选项支持的所有格式。
BR2_GRAPH_OUT=svg make graph-depends
可以通过设置BR2_GRAPH_DEPS_OPTS
环境变量中的选项来控制graph-depends
的行为。可接受的选项有:
选项 | 功能 |
---|---|
--depth N, -d N |
将依赖深度限制为N个级别。默认值0表示没有限制。 |
--stop-on PKG, -s PKG |
在软件包PKG上停止。PKG可以是实际的包名、通配符、关键字virtual(在虚拟包上停止)或关键字host(在主机包上停止)。这个包仍然在图中,但是它的依赖项已经不在图中了。 |
--exclude PKG, -x PKG |
像 --stop on ,但从图中也省略了PKG。 |
--transitive, --no-transitive |
绘制(或不绘制)传递依赖关系。默认情况下不绘制传递依赖关系。 |
--colors R,T,H |
用逗号分隔的颜色列表来绘制根包(R)、目标包(T)和主机包(H)。默认为:浅蓝色、灰色、浅灰色 |
BR2_GRAPH_DEPS_OPTS='-d 3 --no-transitive --colors=red,green,blue' make graph-depends
8.9. 绘制构建持续时间图
当系统的构建花费了很长时间时,有时了解哪些包的构建时间最长是很有用的,可以查看是否可以做些什么来加速构建。为了帮助这种构建时间分析,Buildroot收集每个包的每个步骤的构建时间,并允许从这些数据生成图表。
要在一次构建后生成构建时间图,请运行:
$ make graph-build
这将在output/graphs
中生成一组文件:
文件 | 说明 |
---|---|
build.hist-buld.pdf |
每个包的构建时间直方图,按构建顺序排序。 |
build.hist-duration.pdf |
每个包的构建时间柱状图,按持续时间(最长的首先)排序 |
build.hist-name.pdf |
每个包的构建时间直方图,按包名排序。 |
build.pie-packages.pdf |
每个包构建时间的饼状图 |
build.pie-steps.pdf |
在包构建过程的每个步骤中花费的全局时间的饼状图。 |
这个图构建目标需要安装Python Matplotlib和Numpy库(大多数发行版上安装Python - Matplotlib和Python - Numpy),如果使用的Python版本超过2.7,还需要安装argparse模块(大多数发行版上安装Python -argparse)。
默认情况下,图形的输出格式是PDF,但是可以使用BR2_GRAPH_OU
T环境变量选择另一种格式。唯一的其他格式支持是PNG:
$ BR2_GRAPH_OUT=png make graph-build
8.10. 用图形表示包占用的文件系统大小
当目标系统增长时,有时了解每个Buildroot包对整个根文件系统大小的影响是有用的。为了帮助进行这种分析,Buildroot收集关于每个包安装的文件的数据,并使用这些数据,生成一个图表和CSV文件,详细说明不同包的大小贡献。
要在构建后生成这些数据,请运行:
$ make graph-size
8.11 顶层并行构建
请注意。本节讨论的是一个非常实验性的特性,在一些不寻常的情况下可以实现收支平衡。使用风险自负。
Buildroot一直能够在每个包的基础上使用并行构建:每个包都是由Buildroot使用make -jN(或对非基于构建的构建系统的等效调用)构建的。默认情况下,并行级别是cpu数+ 1,但是可以使用BR2_JLEVEL
配置选项进行调整。
直到2020.02年,Buildroot都是以串行方式构建包的:每个包都是一个接一个地构建的,没有在包之间并行构建。在2020.02年,Buildroot对顶级并行构建提供了实验性支持,通过并行构建没有依赖关系的包,可以节省大量的构建时间。然而,这个特性被标记为实验性的,并且在某些情况下是无效的。
为了使用顶级并行构建,必须:
- 在Buildroot配置中启用选项
BR2_PER_PACKAGE_DIRECTORIES
- 在开始构建Buildroot时使用
make -jN
在内部,BR2_PER_PACKAGE_DIRECTORIES
将启用一种称为每个包目录(per-package directories)的机制,它将有以下效果:
- 分别在
$(O)/per-package/<pkg>/target/
和$(O)/per-package/<pkg>/host/
中使用每个包的目标目录和主机目录,而不是所有包共有的全局目标目录和全局主机目录。这些文件夹将从<pkg>
构建开始时包依赖项的相应文件夹中填充。因此,编译器和所有其他工具将只能看到和访问由<pkg>
显式列出的依赖项安装的文件。 - 在构建结束时,全局目标和主机目录分别位于
$(O)/target
和$(O)/host
中。这意味着在构建期间,这些文件夹将是空的,并且只有在构建的最后才会填充它们。
8.13. 高级用法
8.13.1. 使用在Buildroot外生成的工具链
您可能想为您的目标编译自己的程序或其他没有打包在Buildroot中的软件。为此,您可以使用由Buildroot生成的工具链。
由Buildroot生成的工具链默认位于output/host/
中。使用它最简单的方法是将output/host/bin/
添加到PATH环境变量中,然后使用ARCH-linux-gcc
、ARCH-linux-objdump
、ARCH-linux-ld
等等。
另外,Buildroot还可以通过运行命令make SDK
将工具链和所有选定包的开发文件导出为SDK。这将生成主机目录output/host/
的内容的压缩包,名为<TARGET-TUPLE>_sdk-buildroot.tar.gz
(可以通过设置环境变量BR2_SDK_PREFIX
来覆盖它),并位于输出目录output/images/
中。
然后,当应用程序开发人员希望开发尚未打包为Buildroot包的应用程序时,可以将这个tarball分发给他们。
在提取SDK压缩包时,用户必须运行脚本relocations - SDK .sh
(位于SDK的顶部目录),以确保所有路径都使用新位置更新。
另外,如果你只是想准备SDK而不生成tarball(例如,因为你要移动主机目录,或者自己生成tarball), Buildroot也允许你只用make prepare- SDK
准备SDK而不实际生成一个tarball。
8.13.2. 在Buildroot中使用gdb
Buildroot允许进行交叉调试,调试器在构建机上运行,并与目标机上的gdbserver通信以控制程序的执行。
为实现这一目标:
- 如果使用内部工具链(由Buildroot构建),则必须启用
BR2_PACKAGE_HOST_GDB
、BR2_PACKAGE_GDB
和BR2_PACKAGE_GDB_SERVER
。这将确保构建交叉gdb和gdbserver,并将gdbserver安装到您的目标。 - 如果使用外部工具链,应该启用
BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY
,它将外部工具链包含的gdbserver复制到目标。如果您的外部工具链没有交叉gdb或gdbserver,通过启用与内部工具链后端相同的选项,也可以让Buildroot构建它们。
现在,要开始调试一个名为foo的程序,你应该在目标上运行:
$ gdbserver :2345 foo
这将导致gdbserver在TCP端口2345上侦听来自gdb的连接。
然后,在主机上,您应该使用以下命令行启动cross gdb:
<buildroot>/output/host/bin/<tuple>-gdb -x <buildroot>/output/staging/usr/share/buildroot/gdbinit foo
当然,foo必须在使用调试符号构建的当前目录中可用。通常从构建foo的目录(而不是从output/target/
因为该目录中的二进制文件被剥离)启动这个命令。
<buildroot>/output/staging/usr/share/buildroot/gdbinit
文件将告诉cross gdb在哪里找到目标的库。
最后,从交叉gdb连接到目标:
$ (gdb) target remote <target ip address>:2345
8.13.3. 在Buildroot中使用ccache
ccache是一个编译器缓存。它存储每个编译过程产生的对象文件,并且能够通过使用预先存在的对象文件跳过对相同源文件的将来编译(使用相同的编译器和相同的参数)。在多次从零开始进行几乎相同的构建时,它可以很好地加速构建过程。
ccache的支持集成在Buildroot中。您只需在构建选项中启用启用编译器缓存。这将自动构建ccache,并在每个主机和目标编译中使用它。
缓存位于$HOME/.buildroot-ccache
中。它存储在Buildroot输出目录之外,以便可以由单独的Buildroot构建共享。如果要删除缓存,只需删除此目录。
通过运行make ccache-stats
,您可以获得关于缓存的统计信息(它的大小、命中、错过的次数等)。
make目标ccache-options
和CCACHE_OPTIONS
变量提供对ccache的更通用的访问。例如
# set cache limit size
make CCACHE_OPTIONS="--max-size=5G" ccache-options
# zero statistics counters 0统计计数器
make CCACHE_OPTIONS="--zero-stats" ccache-options
ccache对源文件和编译器选项进行散列。如果编译器选项不同,缓存的对象文件将不会被使用。但是由于许多编译器选项包含一个到登台(staging)目录的绝对路径。因此,在不同的输出目录中构建会导致很多缓存丢失。
为了避免这个问题,buildroot有使用相对路径选项(BR2_CCACHE_USE_BASEDIR
)。这将把所有指向输出目录内的绝对路径重写为相对路径。因此,更改输出目录不再导致缓存丢失。
相对路径的一个缺点是,它们最终也会成为对象文件中的相对路径。因此,例如,除非您先cd到输出目录,否则调试器将不再找到该文件。
请参阅ccache手册的“在不同目录中编译”一节,了解关于绝对路径重写的更多细节。
8.13.4. 下载软件包的位置
Buildroot下载的各种tar包都存储在BR2_DL_DIR
中,默认情况下是dl
目录。如果您想要保留一个完整版本的Buildroot,它与相关的tar包一起工作,您可以复制这个目录。这将允许您用完全相同的版本重新生成工具链和目标文件系统。
如果您维护多个Buildroot树,那么最好有一个共享的下载位置。这可以通过将BR2_DL_DIR
环境变量指向一个目录来实现。如果设置了这个值,那么就会覆盖Buildroot配置中的BR2_DL_DIR
值。下面一行应该添加到<~/.bashrc>
中。
export BR2_DL_DIR=<shared download location>
还可以在.confi
g文件中使用BR2_DL_DIR
选项设置下载位置。与.config
文件中的大多数选项不同,该值由BR2_DL_DIR
环境变量覆盖。
8.13.5. 特定包的安装
运行make <package>
将构建并安装该特定包及其依赖项。
对于依赖于Buildroot基础设施的包,有许多特殊的make目标,可以像这样独立调用:
$ make <package>-<target>
包构建的目标是(按照执行的顺序):
命令/目标 | 说明 |
---|---|
source | 获取源码(下载tar包,克隆源码库等) |
depends | 构建并安装构建软件包所需的所有依赖项。 |
extract | 把源码放在包的构建目录下(解压tar包,复制源码等)。 |
patch | 如果有的话,可以打上补丁 |
configure | 运行配置命令(如果有的话)。 |
build | 运行编译命令 |
install-staging | 目标软件包。如有必要,在暂存目录下运行安装包。 |
install-target | 目标软件包。如有必要,在目标目录下运行软件包的安装。 |
install | target package: 执行前面两个安装命令 host package: 运行安装在主机目录下的软件包 |
此外,还有一些其他有用的make目标:
命令/目标 | 说明 |
---|---|
show-depends | 显示构建软件包所需的一级依赖关系。 |
show-recursive-depends | 递归显示构建包所需的依赖关系。 |
show-rdepends | 显示软件包的一阶反向依赖关系(即直接依赖它的软件包) |
show-recursive-rdepends | 递归显示软件包的反向依赖关系(即直接或间接依赖它的软件包)。 |
graph-depends | 在当前 Buildroot 配置的背景下,生成软件包的依赖关系图。关于依赖关系图的更多细节,请参见本节。 |
graph-rdepends | 生成软件包的反向依赖关系图 (即直接或间接依赖它的软件包) |
dirclean | 删除整个包构建目录 |
reinstall | 重新运行安装命令 |
rebuild | 重新运行编译命令–这只有在使用OVERRIDE_SRCDIR 功能或直接在编译目录下修改文件时才有意义。 |
reconfigure | 重新运行配置命令,然后重建–这只有在使用OVERRIDE_SRCDIR 功能或直接在编译目录下修改文件时才有意义。 |
8.13.6. 在开发期间使用Buildroot
Buildroot的正常操作是下载一个压缩包、解压缩它、配置、编译和安装在这个压缩包中找到的软件组件。源代码在output/build/<package>-<version>
中提取,这是一个临时目录:每当使用make clean
时,该目录将被完全删除,并在下一次make调用时重新创建。即使使用Git或Subversion存储库作为包源代码的输入,Buildroot也会从中创建一个tarball,然后像对待tarball一样工作。
当Buildroot主要用作集成工具来构建和集成嵌入式Linux系统的所有组件时,这种行为非常适合。但是,如果在开发系统的某些组件时使用了Buildroot,那么这种行为就不太方便了:人们可能会希望对一个包的源代码做一点小小的更改,然后能够使用Buildroot快速地重新构建系统。
直接在output/build/<package>-<version>
中进行更改不是一个合适的解决方案,因为这个目录在make clean
时被删除了。
因此,Buildroot为这个用例提供了一种特定的机制:<pkg>_OVERRIDE_SRCDIR
机制。Buildroot读取一个覆盖文件,该文件允许用户告诉Buildroot某些包的源文件的位置。
覆盖文件的默认位置是$(CONFIG_DIR)/local.mk
。由BR2_PACKAGE_OVERRIDE_FILE
配置选项定义。$(CONFIG_DIR)
是Buildroot的 .config
文件的位置,所以是local.mk
默认情况下与.config
文件并行,这意味着:
- 在树内构建的顶层Buildroot源目录中(例如当不使用
O=
时) - 在树外构建的外目录中。(例如:当使用
O=
时)
如果需要不同于这些默认值的位置,可以通过BR2_PACKAGE_OVERRIDE_FILE
配置选项指定。
在这个覆盖文件中,Buildroot期望找到:
<pkg1>_OVERRIDE_SRCDIR = /path/to/pkg1/sources
<pkg2>_OVERRIDE_SRCDIR = /path/to/pkg2/sources
比如:
LINUX_OVERRIDE_SRCDIR = /home/bob/linux/
BUSYBOX_OVERRIDE_SRCDIR = /home/bob/busybox/
当Buildroot发现给定的软件包定义了<pkg>_OVERRIDE_SRCDIR
时,它将不再尝试下载、解压和打补丁。相反,它会直接使用指定目录下的源代码,并且make clean
不会触及这个目录。这允许将Buildroot指向你自己的目录,这些目录可以由Git、Subversion或其他版本控制系统管理。为此,Buildroot会使用rsync将组件的源代码从指定的<pkg>_OVERRIDE_SRCDIR
复制到output/build/<package>-custom/
。
这个机制最好与 make <pkg>-rebuild
和 make <pkg>-reconfigure
目标一起使用。make <pkg>-rebuild all
序列会将 <pkg>_OVERRIDE_SRCDIR
中的源代码 rsync 到 output/build/<package>-custom
中 (由于 rsync 的作用,只复制了修改过的文件),然后重新开始这个包的编译过程。
在上面linux包的例子中,开发者可以在/home/bob/linux
中修改源代码,然后运行:
make linux-rebuild all
并在几秒钟内就能在output/images
中得到更新的Linux内核镜像。
同样,可以对/home/bob/busybox
中的BusyBox源代码进行修改,之后。
make busybox-rebuild
output/images
中的根文件系统映像将包含更新的BusyBox。
大型项目的源代码树通常包含数百或数千个文件,这些文件在构建时并不需要,但会拖慢使用rsync复制源代码的进程。可以选择定义<pkg>_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS
来跳过同步源代码树中的某些文件。例如,当使用 webkitgtk 软件包时,以下内容将从本地 WebKit 源代码树中排除测试和树内构建。
WEBKITGTK_OVERRIDE_SRCDIR = /home/bob/WebKit
WEBKITGTK_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS = \
--exclude JSTests --exclude ManualTests --exclude PerformanceTests \
--exclude WebDriverTests --exclude WebKitBuild --exclude WebKitLibraries \
--exclude WebKit.xcworkspace --exclude Websites --exclude Examples
默认情况下,Buildroot会跳过VCS工件(例如.git和.svn目录)的同步。有些软件包希望在编译过程中使用这些 VCS 目录,例如,为了自动确定版本信息的精确提交参考。如果要以较慢的速度为代价撤销这种内置的过滤,请将这些目录加回来。
LINUX_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS = --include .git
第九章 具体项目的定制
对于一个给定的项目,你可能需要执行的典型动作有:
- 配置Buildroot(包括构建选项和工具链、引导加载程序、内核、包和文件系统映像类型选择)
- 配置其他组件,如Linux内核和BusyBox。
- 自定义生成的目标文件系统
- 在目标文件系统上添加或覆盖文件(使用
BR2_ROOTFS_OVERLAY
)。 - 修改或删除目标文件系统中的文件(使用
BR2_ROOTFS_POST_BUILD_SCRIPT
)。 - 在生成文件系统映像之前运行任意命令(使用
BR2_ROOTFS_POST_BUILD_SCRIPT
)。 - 设置文件权限和所有权(使用
BR2_ROOTFS_DEVICE_TABLE
)。 - 添加自定义设备节点(使用
BR2_ROOTFS_STATIC_DEVICE_TABLE
)
- 在目标文件系统上添加或覆盖文件(使用
- 添加自定义用户账户(使用
BR2_ROOTFS_USERS_TABLES
)。 - 在生成文件系统镜像后运行任意命令(使用
BR2_ROOTFS_POST_IMAGE_SCRIPT
)。 - 为一些软件包添加特定项目的补丁(使用
BR2_GLOBAL_PATCH_DIR
) - 添加项目专用包
关于这种特定于项目的定制的一个重要提示:请仔细考虑哪些更改确实是特定于项目的,哪些更改也对项目之外的开发人员有用。Buildroot社区高度推荐并鼓励对官方Buildroot项目的改进、包和板级支持。当然,有时不可能或不希望上游,因为更改是高度特定的或专有的。
本章描述了如何在Buildroot中进行此类特定项目的自定义,以及如何以一种即使在运行了make clean
之后也能以可重复的方式构建相同映像的方式存储它们。通过遵循推荐的策略,您甚至可以使用相同的Buildroot树来构建多个不同的项目!
9.1. 推荐的目录结构
当为您的项目定制Buildroot时,您将创建一个或多个需要存储在某个地方的特定于项目的文件。尽管大多数这些文件可以放在任何位置,因为它们的路径在Buildroot配置中指定,但Buildroot开发人员推荐一个特定的目录结构,将在本节中描述。
与此目录结构正交的是,您可以选择将此结构本身放置在何处:在Buildroot树内部,或在其外部使用br2-external
树。两个选项都是有效的,由您选择。
+-- board/
| +-- <company>/
| +-- <boardname>/
| +-- linux.config
| +-- busybox.config
| +-- <other configuration files>
| +-- post_build.sh
| +-- post_image.sh
| +-- rootfs_overlay/
| | +-- etc/
| | +-- <some file>
| +-- patches/
| +-- foo/
| | +-- <some patch>
| +-- libbar/
| +-- <some other patches>
|
+-- configs/
| +-- <boardname>_defconfig
|
+-- package/
| +-- <company>/
| +-- Config.in (if not using a br2-external tree)
| +-- <company>.mk (if not using a br2-external tree)
| +-- package1/
| | +-- Config.in
| | +-- package1.mk
| +-- package2/
| +-- Config.in
| +-- package2.mk
|
+-- Config.in (if using a br2-external tree)
+-- external.mk (if using a br2-external tree)
+-- external.desc (if using a br2-external tree)
注意:如果你选择把这个结构放在Buildroot树之外,但放在br2-external
树中,那么<company>
和<boardname>
组件可能是多余的,可以省略。
9.1.1. 实现分层定制
一个用户有几个相关的项目,其中一部分需要相同的定制,这是很常见的。与其在每个项目上重复这些定制,不如使用本节所述的分层定制方法。
Buildroot中几乎所有的自定义方法,比如编译后的脚本和根文件系统覆盖,都接受一个空格分隔的项目列表。指定的项目总是按顺序处理,从左到右。通过创建多个这样的项目,一个用于普通的定制,另一个用于真正特定项目的定制,可以避免不必要的重复。每一层通常都是由board/<company>/
里面的一个单独的目录来体现的。根据你的项目,你甚至可以引入两个以上的层。
一个用户有两个自定义层共同和fooboard的目录结构的例子是。
+-- board/
+-- <company>/
+-- common/
| +-- post_build.sh
| +-- rootfs_overlay/
| | +-- ...
| +-- patches/
| +-- ...
|
+-- fooboard/
+-- linux.config
+-- busybox.config
+-- <other configuration files>
+-- post_build.sh
+-- rootfs_overlay/
| +-- ...
+-- patches/
+-- ...
例如,如果用户将BR2_GLOBAL_PATCH_DIR
配置选项设置为:
BR2_GLOBAL_PATCH_DIR="board/<company>/common/patches board/<company>/fooboard/patches"
那么首先应用普通层的补丁,然后是fooboard层的补丁。
9.2. 将自定义文件保存在Buildroot之外
正如9.1节中简要提到的,“推荐的目录结构”,您可以在两个位置放置特定项目的定制:
- 直接在Buildroot树中,通常使用版本控制系统中的分支来维护它们,这样升级到新的Buildroot版本就很容易了。
- 在Buildroot的外部目录树,使用
b2 -external
机制。这种机制允许将包配方、板支持和配置文件保存在Buildroot树之外,同时仍然将它们很好地集成在构建逻辑中。我们称这个位置为b2 -external
树。本节解释如何使用br2-external
机制,以及在br2-external
树中提供什么。
通过将BR2_EXTERNAL
make变量设置为要使用的br2-external
树的路径,可以告诉Buildroot使用一个或多个br2-external
树。它可以传递给任何Buildroot make调用。它会自动保存在在输出目录中隐藏的.b2-external.mk
文件中。由于这一点,不需要在每次make调用时传递BR2_EXTERNAL
。但是,可以在任何时候通过传递新值来更改它,也可以通过传递空值来删除它。
请注意:br2-external
树的路径可以是绝对的,也可以是相对的。如果它作为相对路径传递,一定要注意它是相对于主Buildroot源目录解释的,而不是相对于Buildroot输出目录解释的。
注意:如果使用的是Buildroot 2016.11之前的br2-external
树,那么在使用Buildroot 2016.11之前需要对其进行转换。请参阅第26.1节“迁移到2016.11”以获得相关帮助。
一些例子:
buildroot/ $ make BR2_EXTERNAL=/path/to/foo menuconfig
从现在开始,从/path/to/foo
定义的 br2-external
树将被使用:
buildroot/ $ make
buildroot/ $ make legal-info
我们可以在任何时候切换到另一个br2-external树:
buildroot/ $ make BR2_EXTERNAL=/where/we/have/bar xconfig
我们也可以使用多个b2-external
树:
buildroot/ $ make BR2_EXTERNAL=/path/to/foo:/where/we/have/bar menuconfig
或禁用任何b2-exteranl
树的使用:
buildroot/ $ make BR2_EXTERNAL= xconfig
9.2.1. 外部br2树的布局
一个外部的br2树必须至少包含这三个文件,在下面的章节中描述:
- external.desc
- external.mk
- Config.in
除了这些强制文件之外,在b2-external
树中可能还有其他可选的内容,比如configs/
或provide/
目录。它们也将在接下来的章节中进行描述。
稍后还将介绍一个完整的示例b2 -外部树布局。
external.desc文件
该文件描述了br2-external树:br2-external树的名称和描述。
该文件的格式是基于行的,每行以一个关键字开始,后跟一个冒号和一个或多个空格,后跟分配给该关键字的值。目前有两个关键字得到认可:
-
name(必选):定义了b2 -外部树的名称。该名称必须只使用集合
[A-Za-z0-9_]
中的ASCII字符;任何其他字符都是禁止的。Buildroot将变量BR2_EXTERNAL_$(NAME)_PATH
设置为br2-external
树的绝对路径,以便您可以使用它来引用您的br2-external
树。这个变量在Kconfig和Makefile中都可用,因此可以使用它来源引Kconfig文件(见下面)和Makefile,这样就可以使用它来包含其他Makefile(见下面)或引用来自br2-external树的其他文件(如数据文件)。注意:由于可以同时使用多个
br2-external
树,Buildroot使用这个名称为每个树生成变量。这个名字是用来识别你br2-external树,所以尽量想出这个名字真的描述br2-external树,为了使它相对独特,这样它就不会与另一个b2 -external树的另一个名称冲突,特别是如果你正计划在某种程度上与第三方分享br2-external树或从第三方使用br2-external树。 -
desc是可选的,它提供了br2-external树的简短描述。它应该适合单行,大部分是自由格式的(见下面),用于显示一个外部br2树的信息(例如,在defconfig文件列表的上面,或作为menuconfig中的提示符);因此,它应该相对简短(40字符可能是一个不错的上限)。描述可以在
BR2_EXTERNAL_$(NAME)_DESC
变量中找到。
名称和相应的BR2_EXTERNAL_$(NAME)_PATH
变量的例子:
9.3. 存储Buildroot配置
可以使用make savedefconfig
命令存储Buildroot配置。
这通过删除默认值下的配置选项来去除Buildroot配置。结果存储在一个名为defconfig的文件中。如果您希望将其保存到另一个位置,可以更改Buildroot配置本身中的BR2_DEFCONFIG
选项,或者使用make savedefconfig BR2_DEFCONFIG=<path-to-defconfig>
调用make。
建议存储这个defconfig的位置是configs/<boardname>_defconfig
。如果遵循此建议,配置将在make help
中列出,并且可以通过运行make <boardname>_defconfig
再次进行设置。
或者,您可以将文件复制到任何其他位置,并使用make defconfig BR2_DEFCONFIG=<path-to-defconfig-file>
重新构建。
9.4. 存储其他组件的配置
BusyBox、Linux 内核、Barebox、U-Boot 和 uClibc 的配置文件也应该被保存起来,如果改变的话。对于这些组件中的每一个,都存在一个Buildroot配置选项,指向一个输入配置文件,例如BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE
。要存储它们的配置,请将这些配置选项设置为要保存配置文件的路径,然后使用下面描述的帮助目标来实际存储配置。
正如第9.1节 “推荐的目录结构 “中所解释的那样,推荐的存储这些配置文件的路径是board/<company>/<boardname>/foo.config
。
确保在更改BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE
等选项之前创建了一个配置文件。否则,Buildroot将尝试访问这个配置文件,该配置文件还不存在,并且将失败。您可以通过运行make linux-menuconfig
等来创建配置文件。
Buildroot提供了一些帮助目标,使保存配置文件更容易:
make linux-update-defconfig
将linux配置保存到BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE
指定的路径。它通过删除默认值简化了配置文件。但是,这只适用于从2.6.33开始的内核。对于早期的内核,使用make linux-update-config
。make busybox-update-config
将busy-box配置保存到BR2_PACKAGE_BUSYBOX_CONFIG
指定的路径。make uclibc-update-config
将 uClibc 配置保存到BR2_UCLIBC_CONFIG
指定的路径。make barebox-update-defconfig
将barebox配置保存到BR2_TARGET_BAREBOX_CUSTOM_CONFIG_FILE
指定的路径make uboot-update-defconfig
将U-Boot的配置保存到BR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE
指定的路径
9.5. 定制生成的目标文件系统
除了通过make *config
修改配置之外,还有一些其他方法可以定制生成的目标文件系统。
推荐的两种方法是根文件系统覆盖和构建后脚本,它们可以共存。
-
根文件系统布局(
BR2_ROOTFS_OVERLAY
)- 文件系统布局是一个文件树,在目标文件系统被构建之后,该文件树直接复制到目标文件系统上。要启用此特性,请将配置选项
BR2_ROOTFS_OVERLAY
(在系统配置 System configuration 菜单中)设置为布局的根目录(the root of the overlay)。您甚至可以指定多个布局,以空格分隔。如果指定一个相对路径,它将相对于Buildroot树的根。版本控制系统的隐藏目录,如.git、.svn、.hg等,称为.empty的文件和以~结尾的文件被排除在副本之外。 - 当启用
BR2_ROOTFS_MERGED_USR
时,布局层不能包含/bin
、/lib
或/sbin
目录,因为Buildroot会将它们作为符号链接创建到/usr
中的相关文件夹。在这种情况下,如果覆盖层中有任何程序或库,它们应该放在/usr/bin
、/usr/sbin
和/usr/lib
中。
- 文件系统布局是一个文件树,在目标文件系统被构建之后,该文件树直接复制到目标文件系统上。要启用此特性,请将配置选项
-
后期构建脚本 (
BR2_ROOTFS_POST_BUILD_SCRIPT
)-
后构建脚本是在Buildroot构建所有选定软件之后,但在rootfs映像组装之前调用的shell脚本。要启用这个特性,请在配置选项
BR2_ROOTFS_POST_BUILD_SCRIPT
(在系统配置菜单中)中指定一个空格分隔的后构建脚本列表。如果指定一个相对路径,它将相对于Buildroot树的根。 -
使用构建后脚本,您可以删除或修改目标文件系统中的任何文件。但是,您应该小心使用此特性。每当您发现某个包生成了错误或不需要的文件时,您应该修复该包,而不是使用一些构建后的清理脚本来解决它。
如9.1节“推荐的目录结构”所示,此脚本推荐的路径为
board/<company>/<boardname>/post_build.sh
。 -
构建后脚本以主Buildroot树作为当前工作目录运行。目标文件系统的路径作为每个脚本的第一个参数传递。如果配置选项
BR2_ROOTFS_POST_SCRIPT_ARGS
不是空的,那么这些参数也将传递给脚本。所有脚本都将传递相同的参数集,不可能向每个脚本传递不同的参数集。 -
此外,您还可以使用以下环境变量:
BR2_CONFIG
: Buildroot.config
文件的路径- HOST_DIR、STAGING_DIR、TARGET_DIR:参见17.5.2节“通用包引用”
- BUILD_DIR:解压缩和构建包的目录
- BINARIES_DIR:存储所有二进制文件(即镜像)的位置
- BASE_DIR:基本输出目录
-
9.5.1. 设置文件权限和所有权,并添加自定义设备节点
有时需要在文件或设备节点上设置特定的权限或所有权。例如,某些文件可能需要由根用户拥有。由于后构建脚本不是以root用户的身份运行的,所以您不能从那里执行这样的更改,除非您使用后构建脚本中的显式fakeroot。
相反,Buildroot为所谓的权限表提供了支持。要使用这个特性,请将配置选项BR2_ROOTFS_DEVICE_TABLE
设置为一个空格分隔的权限表列表,以及遵循makedev语法)的规则文件。
如果使用的是静态设备表(即不使用devtmpfs、mdev或(e)udev),那么可以使用相同的语法在所谓的设备表中添加设备节点。要使用这个特性,请将配置选项BR2_ROOTFS_STATIC_DEVICE_TABLE
设置为一个空格分隔的设备表列表。
如9.1节“推荐的目录结构”所示,推荐的位置为board/<company>/<boardname>/
。
需要注意的是,如果特定权限或设备节点与特定应用程序相关,则应该在包的.mk文件中设置变量FOO_PERMISSIONS
和FOO_DEVICES
(参见17.5.2节“通用包引用”)。