Skip to the content.

Buildroot用户手册

目录

I. 开始

1. 关于Buildroot

2. 系统需求

2.1. 必要的包

2.2. 可选的包

3. 获取Buildroot

4. Buildroot快速开始

5. 社区资源

II. 用户指南

6. Buildroot配置

6.1. 交叉编译工具链

6.2. /dev管理

6.3. 初始化系统

7. 其他组件的配置

8. 一般Buildroot用法

8.1. make tips

8.2. 理解什么时候需要完全重建

8.3. 理解如何重建包

8.4. 离线构建

8.5. 树外构建

8.6. 环境变量

8.7. 高效地处理文件系统映像

8.8. 关于包的详细信息

8.9. 绘制包之间的依赖关系图

8.10. 绘制构建持续时间

8.11. 绘制包对文件系统存储的占用

8.12. 顶层并行构建

8.13. 高级用法

9. 具体项目的定制

9.1. 推荐的目录结构

9.2. 在Buildroot之外保持自定义

9.3. 存储Buildroot配置

9.4. 存储其他组件的配置信息

9.5. 自定义生成的目标文件系统

9.6. 添加自定义用户账户

9.7. 创建映像后的自定义

9.8. 添加特定于项目的补丁

9.9. 添加特定于项目的包

9.10. 快速指南存储您的项目特定的自定义

10. 集成的话题

10.1. Systemd

10.2. 在Buildroot中使用SELinux

11. 常见问题及故障处理

11.1. 启动网络后boot挂起…

11.2. 为什么目标上没有编译器?

11.3. 为什么target上没有开发文件?

11.4. 为什么没有目标的资料?

11.5. 为什么有些包在Buildroot配置菜单中不可见?

11.6. 为什么不使用目标目录作为chroot目录呢?

11.7. 为什么Buildroot不能生成二进制包(.deb, .ipkg…)

11.8. 如何加快构建过程?

12. 已知的问题

13. 法律公告及licensing

13.1. 遵循开源许可协议

13.2. 符合Buildroot许可证

14. Beyond Buildroot

14.1. 引导生成的映像

14.2. Chroot

III. 开发人员指南

15. Buildroot是如何工作的

16. 编码风格

16.1. Config.in文件

16.2. .mk文件

16.3. genimage.cfg文件

16.4. 文档

16.5. 支持脚本

17. 添加对特定开发板的支持

18. 向Buildroot添加新包

18.1. 包目录

18.2. 配置文件

18.3. .mk文件

18.4. .hash文件

18.5. SNNfoo启动脚本

18.6. 具有特定构建系统的包的基础架构

18.7. 基于autotools的包的基础架构

18.8. 基于cmake的包的基础架构

18.9. Python包的基础架构

18.10. 基于LuaRocks的软件包的基础架构

18.11. Perl/CPAN包的基础架构

18.12. 虚拟包的基础架构

18.13. 包的基础架构,使用kconfig配置文件

18.14. 基于rebar的包的基础架构

18.15. 基于Waf的包的基础架构

18.16. 基于Meson的包的基础架构

18.17. 基于Cargo的包的基础架构

18.18. Go包的基础架构

18.19. 基于QMake的包的基础架构

18.20. 构建内核模块的包的基础架构

18.21. asciidoc文档的基础架构

18.22. 特定于Linux内核包的基础架构

18.23. 在不同的构建步骤中可用的钩子

18.24. 与包的Gettext集成和交互

18.25. 提示和技巧

18.26. 结论

19. 为软件包打补丁

19.1. 提供补丁

19.2. 如何应用补丁

19.3. 软件包补丁的格式和许可

19.4. 额外的补丁文档

20. 下载基础架构

21. 调试Buildroot

22. 为Buildroot贡献

22.1. 重现、分析和修复bug

22.2. 分析和修复自动构建失败

22.3. 检查和测试补丁

22.4. 处理待办事项列表中的待办事项

22.5. 提交补丁

22.6. 报告问题/bug或寻求帮助

22.7. 使用运行时测试框架

23. 开发者文件和获取开发者

24. Release Engineering

24.1. Releases

24.2. Development

IV. 附录

25. Makedev语法文档

26. Makeusers语法文档

26.1. 自动UID和GID的警告

27. 从较旧的Buildroot版本迁移

27.1. 一般方法

27.2. 迁移到2016.11

27.3. 迁移到2017.08

List of Examples

18.1. 配置脚本: divine package

18.2. 配置脚本: imagemagick package:

Buildroot 2023.05.1手动生成于2023-07-17 15:07:36 UTC,由git版本7814dbce15生成

Buildroot手册是由Buildroot开发人员编写的。它在GNU通用公共许可证(version 2)下授权。请参阅Buildroot源代码中的COPYING文件以获得本许可证的全文。

Copyright © The Buildroot developers buildroot@buildroot.org

Part I. 开始

Chapter 1. About Buildroot


Buildroot是一个工具,它使用交叉编译简化并自动化为嵌入式系统构建完整Linux系统的过程。

为了实现这一点,Buildroot能够为您的目标生成交叉编译工具链、根文件系统、Linux内核映像和引导加载程序。Buildroot可以独立地用于这些选项的任何组合(例如,您可以使用现有的交叉编译工具链,并仅使用Buildroot构建根文件系统)。

Buildroot主要用于处理嵌入式系统。嵌入式系统通常使用的处理器不是大家习惯于在自己的PC上使用的常规x86处理器。它们可以是PowerPC处理器、MIPS处理器、ARM处理器等。

Buildroot支持许多处理器及其变体。它还提供了几种现成的主板的默认配置。除此之外,许多第三方项目都是基于或在Buildroot之上开发他们的BSP[1]或SDK[2]。

[1] BSP: Board Support Package

[2] SDK: Software Development Kit

Chapter 2. System requirements


Buildroot设计用于在Linux系统上运行。

虽然Buildroot本身将构建编译所需的大多数主机包,但预计已经在主机系统上安装了某些标准Linux实用程序。下面是必选包和可选包的概述(注意,包的名称可能因发行版而异)。

2.1. Mandatory packages


2.2. Optional packages


Chapter 3. Getting Buildroot


Buildroot每3个月发布一次,分别在2月、5月、8月和11月。发行版本号的格式为YYYY.MM,比如2013.02,2014.08。

发布压缩包可在 http://buildroot.org/downloads/.

为了方便您,在Buildroot源代码树的support/misc/Vagrantfile中有一个Vagrantfile,可以快速设置一个虚拟机,并使用所需的依赖项开始运行。

如果你想在Linux或Mac Os X上设置一个隔离的buildroot环境,将这行代码粘贴到你的终端:

curl -O https://buildroot.org/downloads/Vagrantfile; vagrant up

如果你使用的是Windows系统,将以下代码粘贴到powershell中:

(new-object System.Net.WebClient).DownloadFile(
"https://buildroot.org/downloads/Vagrantfile","Vagrantfile");
vagrant up

如果你想继续开发,可以使用每日快照或克隆Git存储库。有关更多详细信息,请参阅Buildroot网站的下载页

Chapter 4. Buildroot quick start


重要: 你可以并且应该以一个普通用户的身份构建一切。不需要拥有root权限就可以配置和使用Buildroot。通过以普通用户身份运行所有命令,可以保护系统免受包在编译和安装期间行为不佳的影响。

使用Buildroot的第一步是创建配置。Buildroot有一个很好的配置工具,类似于您可以在Linux kernelBusyBox中找到的配置工具。

从buildroot目录运行

$ make menuconfig

对于原始的基于curses的配置器,或

$ make nconfig

对于新的基于curses的配置器,或者

$ make xconfig

对于基于qt的配置器,或者

$ make gconfig

对于基于GTK的配置器。

所有这些“make”命令都需要构建一个配置实用工具(包括接口),所以你可能需要为配置实用工具使用的相关库安装“development”包。参考第2章,系统需求了解更多细节,特别是可选的要求来获取你最喜欢的界面的依赖项。

对于配置工具中的每个菜单项,您可以找到描述该项目的相关帮助。请参阅Chapter 6, Buildroot configuration了解一些具体配置的细节。

一旦所有配置完成,配置工具会生成一个包含整个配置的.config文件。这个文件将由顶层的Makefile读取。

要启动构建过程,只需运行:

$ make

默认情况下,Buildroot不支持顶层并行构建,因此无需运行make -jN。不过,也有对顶层并行构建的实验性支持,参见8.12节,“顶层并行构建”

make命令通常会执行以下步骤:

Buildroot的输出存储在单个目录output/中。这个目录包含以下几个子目录:

这些命令,make menuconfig|nconfig|gconfig|xconfigmake,是最基本的命令,允许轻松快速地生成符合您需求的图像,具有您启用的所有功能和应用程序。

关于“make”命令用法的更多细节见8.1节, “make tips”

Chapter 5. Community resources


与任何开源项目一样,Buildroot在社区内外都有不同的共享信息的方式。

如果您正在寻求帮助,想了解Buildroot或为项目做出贡献,这些方法都可能使您感兴趣。

邮件列表

Buildroot有一个邮件列表供讨论和开发。它是Buildroot用户和开发人员交互的主要方法。

只有Buildroot邮件列表的订阅者才能向此列表发送邮件。您可以通过邮件列表信息页订阅。

发送到邮件列表的邮件也可以在邮件列表存档中找到,可以通过Mailmanlore.kernel.org获得。

IRC

Buildroot IRC频道#buildroot托管在OFTC上。这是一个很有用的地方,可以快速提出问题或讨论某些主题。

在IRC上寻求帮助时,请使用代码分享网站分享相关的日志或代码片段,例如https://paste.ack.tf/

请注意,对于某些问题,发送到邮件列表可能更好,因为它将接触到更多的人,包括开发人员和用户。

Bug tracker

Buildroot中的bug可以通过邮件列表报告,或者通过Buildroot bugtracker报告。在创建bug报告之前,请参阅第22.6节,“报告issue/bug或获得帮助”

Wiki

Buildroot wiki页面托管在eLinux wiki上。它包含一些有用的链接,过去和即将发生的事件的概述,以及待办事项列表。

Patchwork

Patchwork是一个基于web的补丁跟踪系统,旨在促进对开源项目的贡献和管理。已发送到邮件列表的补丁会被系统“捕获”,并出现在网页上。任何引用补丁的评论也会添加到补丁页面中。有关Patchwork的更多信息,请参见http://jk.ozlabs.org/projects/patchwork/

Buildroot的Patchwork网站主要供Buildroot的维护者使用,以确保不会遗漏补丁。Buildroot补丁审阅者也使用它(参见第22.3.1节,“从Patchwork中应用补丁”)。然而,由于该网站在一个干净简洁的web界面中暴露了补丁及其相应的评审注释,因此它对所有Buildroot开发人员都很有用。

Buildroot补丁管理界面可以在https://patchwork.ozlabs.org/project/buildroot/list/上找到.

Part II. 用户指南

Chapter 6. Buildroot configuration


make *config中的所有配置选项都有一个帮助文本,提供了有关该选项的详细信息。

make *config命令也提供了一个搜索工具。阅读不同前端菜单中的帮助信息以了解如何使用它:

搜索结果显示匹配项的帮助信息。在menuconfig中,左侧列中的数字提供了对应条目的快捷方式。只需键入此数字即可直接跳转到条目,或者在由于缺少依赖项而无法选择条目时跳转到包含菜单。

尽管菜单结构和条目的帮助文本应该足够一目了然,但许多主题需要额外的解释,这些解释在帮助文本中不容易涉及,因此将在以下几节中讨论。

6.1. Cross-compilation toolchain


编译工具链是允许您为系统编译代码的一组工具。它由一个编译器(在我们的例子中,gcc),二进制utils,如汇编器和链接器(在我们的例子中,binutils)和一个C标准库(例如GNU Libc, uClibc-ng)组成。

安装在开发平台上的系统当然已经有了编译工具链,您可以使用它来编译在系统上运行的应用程序。如果你使用的是PC,你的编译工具链在x86处理器上运行,并为x86处理器生成代码。在大多数Linux系统下,编译工具链使用GNU libc (glibc)作为C标准库。这个编译工具链被称为“宿主编译工具链”。运行它和您工作的机器称为“主机系统”[3]。

编译工具链是由你的发行版提供的,Buildroot和它没有任何关系(只是用它来构建交叉编译工具链和在开发主机上运行的其他工具)。

如上所述,系统附带的编译工具链在宿主系统上运行并为处理器生成代码。由于您的嵌入式系统具有不同的处理器,您需要一个交叉编译工具链——一个在主机系统上运行但为目标系统(和目标处理器)生成代码的编译工具链。例如,如果您的主机系统使用x86,而目标系统使用ARM,则主机上的常规编译工具链在x86上运行并为x86生成代码,而交叉编译工具链在x86上运行并为ARM生成代码。

Buildroot为交叉编译工具链提供了两种解决方案:

这两种解决方案之间的选择是通过Toolchain菜单中的Toolchain Type选项来完成的。一旦选择了一个解决方案,就会出现许多配置选项,下面各节将详细介绍。

6.1.1. Internal toolchain backend

内部工具链后端是Buildroot在为目标嵌入式系统构建用户空间应用程序和库之前自行构建交叉编译工具链的后端。

这个后端支持几个C库: uClibc-ng, glibcmusl.

一旦选择了这个后端,就会出现许多选项。其中最重要的几个允许:

值得注意的是,每当修改其中一个选项时,都必须重新构建整个工具链和系统。请参阅8.2节,“了解何时需要完全重建”

这个后端的优点:

这个后端的缺点:

6.1.2. External toolchain backend

外部工具链后端允许使用现有的预构建的交叉编译工具链。Buildroot知道许多著名的交叉编译工具链(来自ARM的LinaroSourcery CodeBench 用于ARM、x86-64、PowerPC和MIPS,并能够自动下载它们,或者可以指向自定义工具链,可以下载或在本地安装。

然后,你有三种使用外部工具链的解决方案:

我们的外部工具链支持已经使用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

这个后端的优点:

这个后端的缺点:

6.1.3. Build an external toolchain with Buildroot

Buildroot内部工具链选项可用于创建外部工具链。以下是构建内部工具链的一系列步骤,并将其打包以供Buildroot本身(或其他项目)重用。

创建一个新的Buildroot配置,详细信息如下:

然后,我们可以触发构建,并让Buildroot生成一个SDK。这将方便地为我们生成一个包含我们的工具链的压缩包:

make sdk

这将在$(O)/images中生成SDK压缩包,其名称类似于arm-buildroot-linux-uclibcgnueabi_sdk-buildroot.tar.gz。保存这个tar包,因为它现在是工具链,您可以在其他Buildroot项目中作为外部工具链重用。

在其他Buildroot项目中,在Toolchain菜单中:

外部工具链包装器

当使用外部工具链时,Buildroot生成一个包装器程序,它透明地将适当的选项(根据配置)传递给外部工具链程序。如果你需要调试这个包装器来检查到底传递了什么参数,你可以将环境变量BR2_DEBUG_WRAPPER设置为以下任意一个:

6.2. /dev management


在Linux系统中,/dev目录包含一些特殊的文件,称为设备文件,允许用户空间应用程序访问由Linux内核管理的硬件设备。如果没有这些设备文件,用户空间应用程序将无法使用硬件设备,即使Linux内核正确识别了这些设备。

System configuration, 和/dev management目录下,Buildroot提供了四种不同的解决方案来处理/dev目录:

Buildroot开发者的建议是,从Dynamic using devtmpfs only的解决方案开始,直到你需要在设备添加/删除时通知用户空间,或者如果需要固件,在这种情况下,Dynamic using devtmpfs + mdev通常是一个很好的解决方案。

注意,如果选择systemd作为init系统,/dev管理将由systemd提供的udev程序执行。

6.3. init system


init程序是内核启动的第一个用户空间程序(PID为1),负责启动用户空间服务和程序(例如:web服务器、图形应用程序、其他网络服务器,等等)。

Buildroot允许使用三种不同类型的init系统,可以从System configurationInit System中选择:

Buildroot开发者推荐的解决方案是使用BusyBox init,因为它对于大多数嵌入式系统来说已经足够了。systemd可以用于更复杂的情况。


[3] 这个术语与GNU configure所使用的不同,在GNU configure中,主机是运行应用程序的机器(通常与target相同)。

Chapter 7. Configuration of other components


在尝试修改下面的任何组件之前,请确保您已经配置了Buildroot本身,并启用了相应的包。

BusyBox

如果你已经有一个BusyBox配置文件,你可以直接使用BR2_PACKAGE_BUSYBOX_CONFIG在Buildroot配置中指定这个文件。否则,Buildroot将从默认的BusyBox配置文件启动。

要对配置进行后续更改,请使用make busybox-menuconfig打开BusyBox配置编辑器。

也可以通过环境变量指定BusyBox配置文件,但不推荐这样做。请参阅第8.6节,环境变量了解更多细节。

uClibc

uClibc的配置与BusyBox的配置相同。用于指定现有配置文件的configuration变量是BR2_UCLIBC_CONFIG。进行后续更改的命令是make uclibc-menuconfig

Linux kernel

如果你已经有了一个内核配置文件,你可以使用BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG直接在Buildroot配置中指定这个文件。

如果你还没有内核配置文件,你可以使用BR2_LINUX_KERNEL_USE_DEFCONFIG在Buildroot配置中指定defconfig,或者使用BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG创建一个空文件并指定它作为自定义配置文件。

要对配置进行后续更改,请使用make linux-menuconfig打开Linux配置编辑器。

Barebox

Barebox的配置与Linux内核的配置相同。对应的配置变量是BR2_TARGET_BAREBOX_USE_CUSTOM_CONFIGBR2_TARGET_BAREBOX_USE_DEFCONFIG。要打开配置编辑器,使用make barebox-menuconfig

U-Boot

U-Boot(2015.04或更新版本)的配置方式与Linux内核相同。对应的配置变量是BR2_TARGET_UBOOT_USE_CUSTOM_CONFIGBR2_TARGET_UBOOT_USE_DEFCONFIG。要打开配置编辑器,使用make uboot-menuconfig

Chapter 8. General Buildroot usage


8.1. make tips


这是一组可以帮助你充分利用Buildroot的技巧。

显示make执行的所有命令: 

$ make V=1 <target>

使用defconfig显示板的列表: 

$ make list-defconfigs

显示所有可用的目标: 

$ make help

并不是所有的targets都是可用的,.config文件中的一些设置可能会隐藏一些targets:

清理: 当任何体系结构或工具链配置选项发生更改时,都需要显式清理。

删除所有构建产品(包括构建目录、宿主、过渡树和目标树、图像和工具链):

$ make clean

生成手册: 目前的手册来源位于docs/manual目录中。生成手册:

$ make manual-clean
$ make manual

手动输出将在output/docs/manual中生成。

注意

为新target重置Buildroot:以删除所有构建产品以及配置:

$ make distclean

注意. 如果启用了ccache,运行make cleandistclean不会清空Buildroot使用的编译器缓存。要删除它,请参阅8.13.3节,“在Buildroot中使用ccache

转储内部的make变量:可以转储已知要创建的变量及其值:

$ make -s printvars VARS='VARIABLE1 VARIABLE2'
VARIABLE1=value_of_variable
VARIABLE2=value_of_variable

可以使用一些变量调整输出:

例如:

$ make -s printvars VARS=BUSYBOX_%DEPENDENCIES
BUSYBOX_DEPENDENCIES=skeleton toolchain
BUSYBOX_FINAL_ALL_DEPENDENCIES=skeleton toolchain
BUSYBOX_FINAL_DEPENDENCIES=skeleton toolchain
BUSYBOX_FINAL_PATCH_DEPENDENCIES=
BUSYBOX_RDEPENDENCIES=ncurses util-linux
$ make -s printvars VARS=BUSYBOX_%DEPENDENCIES QUOTED_VARS=YES
BUSYBOX_DEPENDENCIES='skeleton toolchain'
BUSYBOX_FINAL_ALL_DEPENDENCIES='skeleton toolchain'
BUSYBOX_FINAL_DEPENDENCIES='skeleton toolchain'
BUSYBOX_FINAL_PATCH_DEPENDENCIES=''
BUSYBOX_RDEPENDENCIES='ncurses util-linux'
$ make -s printvars VARS=BUSYBOX_%DEPENDENCIES RAW_VARS=YES
BUSYBOX_DEPENDENCIES=skeleton toolchain
BUSYBOX_FINAL_ALL_DEPENDENCIES=$(sort $(BUSYBOX_FINAL_DEPENDENCIES) $(BUSYBOX_FINAL_PATCH_DEPENDENCIES))
BUSYBOX_FINAL_DEPENDENCIES=$(sort $(BUSYBOX_DEPENDENCIES))
BUSYBOX_FINAL_PATCH_DEPENDENCIES=$(sort $(BUSYBOX_PATCH_DEPENDENCIES))
BUSYBOX_RDEPENDENCIES=ncurses util-linux

加引号的变量的输出可以在shell脚本中重用,例如:

$ eval $(make -s printvars VARS=BUSYBOX_DEPENDENCIES QUOTED_VARS=YES)
$ echo $BUSYBOX_DEPENDENCIES
skeleton toolchain

8.2. Understanding when a full rebuild is necessary


当通过make menuconfigmake xconfig或其他配置工具更改系统配置时,Buildroot不会尝试检测应该重建系统的哪些部分。在某些情况下,Buildroot应该重建整个系统,在某些情况下,只重建特定的包子集。但是以完全可靠的方式检测它是非常困难的,因此Buildroot的开发人员决定不尝试这样做。

相反,用户有责任知道何时需要完全重建。作为提示,这里有一些经验法则可以帮助你理解如何使用Buildroot:

一般来说,当遇到构建错误,并且不确定所做配置更改的潜在后果时,请完全重新构建。如果你遇到了相同的构建错误,那么你可以肯定这个错误与包的部分重构无关,如果这个错误发生在来自官方Buildroot的包上,不要犹豫,报告这个问题!随着你使用Buildroot的经验的发展,你将逐渐了解何时真正需要完全重建,并将节省越来越多的时间。

作为参考,完整的重建可以通过运行:

$ make clean all

8.3. Understanding how to rebuild packages


Buildroot用户最常见的问题之一是如何重建给定的包或如何删除包而不从头开始重建所有内容。

Buildroot不支持删除包而不从头开始重建。这是因为Buildroot不会跟踪哪个包安装了output/stagingoutput/target目录中的哪些文件,或者哪个包会根据另一个包的可用性进行不同的编译。

从头开始重建单个包的最简单方法是在output/build中删除它的构建目录。然后,Buildroot将从头重新提取、重新配置、重新编译和重新安装此包。你可以使用make <package>-dirclean命令让buildroot执行此操作。

另一方面,如果你只想从编译步骤重新启动包的构建过程,你可以运行make <package>-rebuild。它会重新启动包的编译和安装,但不是从零开始:它基本上重新执行包中的makemake install,因此它只会重建更改的文件。

如果你想从配置步骤重新启动包的构建过程,你可以运行make <package>-reconfigure。它将重新启动配置、编译和安装包。

虽然<package>-rebuild意味着<package>-reinstall<package>-reconfigure意味着<package>-rebuild,但这些目标和<package>都只作用于上述包,而不会触发重新创建根文件系统映像。如果需要重新创建根文件系统,另外应该运行makemake all

在内部,Buildroot创建所谓的stamp文件来跟踪每个包的构建步骤已经完成。它们存储在包构建目录output/build/<package>-<version>/中,并被命名为.stamp_<step-name>。上面详细描述的命令只是操作这些stamp文件,以强制Buildroot重新启动包构建过程的特定步骤集。

关于包专用make目标的更多细节将在8.13.5节,“特定于包的生成目标”中解释。

8.4. Offline builds


如果你想进行离线构建,只是想下载之前在配置器中选择的所有源代码(menuconfig、nconfig、xconfig或gconfig),然后执行:

$ make source

你现在可以断开连接或将dl目录的内容复制到build-host。

8.5. Building out-of-tree


默认情况下,Buildroot构建的所有内容都存储在Buildroot树的output目录中。

Buildroot还支持使用类似于Linux内核的语法从树中构建。要使用它,在make命令行中添加O=<directory>:

$ make O=/tmp/build menuconfig

或:

$ cd /tmp/build; make O=$PWD -C path/to/buildroot menuconfig

所有输出文件都位于/tmp/build目录下。如果O路径不存在,Buildroot会创建它。

注意: O路径可以是绝对路径也可以是相对路径,但如果它作为相对路径传递,重要的是要注意,它是相对于主Buildroot源目录进行解释的,不是当前工作目录。

当使用树外构建时,Buildroot的.config和临时文件也存储在输出目录中。这意味着,只要使用唯一的输出目录,您就可以使用相同的源代码树并行运行多个构建。

为了方便使用,Buildroot在输出目录中生成一个Makefile包装器——因此在第一次运行后,你不再需要传递O=<…>-C <…>,只需运行(在输出目录中):

$ make <target>

8.6. Environment variables


当将某些环境变量传递给make或在环境中设置时,Buildroot 还支持一些环境变量:

一个使用位于顶层目录和$HOME中的配置文件的示例:

$ make UCLIBC_CONFIG_FILE=uClibc.config BUSYBOX_CONFIG_FILE=$HOME/bb.config

如果你想使用默认的gccg++以外的编译器在你的主机上构建辅助二进制文件,那么就去做吧

$ make HOSTCXX=g++-4.3-HEAD HOSTCC=gcc-4.3-HEAD

8.7. Dealing efficiently with filesystem images


文件系统映像可能非常大,这取决于你选择的文件系统、包的数量、是否配置了空闲空间……然而,文件系统映像中的某些位置可能是空的(例如,长时间运行的0);这种文件称为稀疏文件(sparse file)。

大多数工具都可以高效地处理sparse文件,并且只存储或写入sparse文件中非空的部分。

例如:

其他工具可能有类似的选项。请查阅各自的手册页。

如果你需要存储文件系统映像(例如从一台机器传输到另一台机器),或者需要发送它们(例如发送到Q&A团队),你可以使用稀疏文件。

但是请注意,当使用稀疏模式dd时,将文件系统映像刷新到设备可能会导致文件系统损坏(例如,ext2文件系统的块位图可能损坏;或者,如果文件系统中有稀疏文件,这些部分在回读时可能不是全0)。你应该只在构建机器上处理文件时使用sparse文件,而不是将它们传输到将在目标上使用的实际设备时。

8.8. Details about packages


Buildroot可以生成一个JSON简介,描述当前配置中启用的一组包,以及它们的依赖项、许可证和其他元数据。这个JSON简介是使用show-info make目标生成的:

make show-info

Buildroot还可以使用pkg-stats make目标生成关于包的详细信息,以HTML和JSON输出。这些细节包括已知的cve(安全漏洞)是否会影响当前配置中的包。它还会显示这些包是否有更新的上游版本。

make pkg-stats

8.9. Graphing the dependencies between packages


Buildroot的工作之一是知道包之间的依赖关系,并确保它们按照正确的顺序构建。这些依赖项有时相当复杂,对于给定的系统,通常不容易理解为什么Buildroot将这样或那样的包引入构建中。

为了帮助理解依赖关系,从而更好地理解嵌入式Linux系统中不同组件的角色,Buildroot能够生成依赖关系图。

要生成你编译的整个系统的依赖关系图,只需运行:

make graph-depends

你可以在output/graphs/graph-depends.pdf中找到生成的图形。

如果你的系统非常大,依赖关系图可能会过于复杂和难以阅读。因此,可以仅为给定的包生成依赖图:

make <pkg>-graph-depends

你可以在output/graph/<pkg>-graph-depends.pdf中找到生成的图形。

请注意,依赖图是使用Graphviz项目中的dot工具生成的,要使用此功能,您必须在系统上安装该工具。在大多数发行版中,它都以graphviz包的形式提供。

默认情况下,依赖关系图以PDF格式生成。然而,通过传递BR2_GRAPH_OUT环境变量,你可以切换到其他输出格式,例如PNG、PostScript或SVG。支持dot工具的-T选项支持的所有格式。

BR2_GRAPH_OUT=svg make graph-depends

graph-depends的行为可以通过在BR2_GRAPH_DEPS_OPTS环境变量中设置选项来控制。可接受的选项有:

BR2_GRAPH_DEPS_OPTS='-d 3 --no-transitive --colors=red,green,blue' make graph-depends

8.10. Graphing the build duration


当系统的构建需要很长时间时,了解哪些包构建时间最长,看看是否可以采取措施加快构建速度,这有时是有用的。为了帮助这种构建时间分析,Buildroot收集每个包的每个步骤的构建时间,并允许从这些数据生成图形。

要在构建后生成构建时间图,请运行:

make graph-build

这将在output/graphs中生成一组文件:

这个graph-build目标需要安装Python Matplotlib和Numpy库(在大多数发行版上是python-matplotlibpython-numpy),如果你使用的Python版本高于2.7(在大多数发行版上是 python-argparse),还需要安装argparse模块。

默认情况下,图形的输出格式是PDF,但可以使用BR2_GRAPH_OUT环境变量选择不同的格式。唯一支持的其他格式是PNG:

BR2_GRAPH_OUT=png make graph-build

8.11. Graphing the filesystem size contribution of packages


当你的目标系统增长时,有时候了解每个Buildroot包对整个根文件系统存储的占用是很有用的。为了帮助进行这样的分析,Buildroot收集关于每个包安装的文件的数据,并使用这些数据,生成一个图表和CSV文件,详细说明不同包的存储占用。

要在构建后生成这些数据,请运行:

make graph-size

这将生成:

这个graph-size目标需要安装Python Matplotlib库(在大多数发行版上是python-matplotlib),如果你使用的Python版本高于2.7(在大多数发行版上是python-argparse),还需要安装argparse模块。

就像持续时间图一样,支持使用BR2_GRAPH_OUT环境变量来调整输出文件格式。请参阅第8.9节,“绘制包之间的依赖关系”以了解这个环境变量的详细信息。

此外,可以设置环境变量BR2_GRAPH_SIZE_OPTS来进一步控制生成的图。可接受的选项有:

注意. 收集到的文件系统大小的数据只有在完全重建后才有意义。在使用make graph-size之前,请确保运行make clean all

要比较两个不同Buildroot编译的根文件系统大小,例如在调整配置后或切换到另一个Buildroot版本时,使用size-stats-compare脚本。它以两个file-size-stats.csv文件(由make graph-size生成)作为输入。有关更多细节,请参阅此脚本的帮助文本:

utils/size-stats-compare -h

8.12. Top-level parallel build


注意. 本节讨论的是一个实验性的特性,在某些正常情况下可以达到收支平衡。使用风险自负。

Buildroot始终能够在每个包的基础上使用并行构建:每个包都是由Buildroot使用make -jN(或非基于make的构建系统的等效调用)构建的。并行度默认为cpu数量+ 1,但可以使用BR2_JLEVEL配置选项进行调整。

然而,直到2020.02,Buildroot都在以串行方式构建包:每个包都是一个接一个地构建,而不是在包之间并行构建。截至2020.02,Buildroot实验性地支持了顶层并行构建,通过并行构建没有依赖关系的包,可以显著节省构建时间。然而,该功能被标记为试验性的,已知在某些情况下不起作用。

为了使用顶层并行构建,必须:

  1. 在Buildroot配置中启用BR2_PER_PACKAGE_DIRECTORIES选项
  2. 在启动Buildroot构建时使用make -jN

在内部,BR2_PER_PACKAGE_DIRECTORIES将启用一种名为per-package directories的机制,它将产生以下效果:

8.13. Advanced usage


8.13.1. Using the generated toolchain outside 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包的应用程序时,可以将此压缩包分发给应用程序开发人员。

在提取SDK压缩包后,用户必须运行脚本relocate-sdk.sh(位于SDK的顶部目录),以确保所有路径都更新为新的位置。

或者,如果你只是想准备SDK而不生成压缩包(例如,因为你只是移动host目录,或者将自己生成压缩包),Buildroot也允许你只使用make prepare-sdk来准备SDK而不实际生成压缩包。

为了方便你,通过选择BR2_PACKAGE_HOST_ENVIRONMENT_SETUP选项,你可以在output/host/中安装一个environment-setup脚本,也就是在你的SDK中。这个脚本可以通过. your/sdk/path/environment-setup来导出一些环境变量,这些变量将有助于使用Buildroot sdk交叉编译你的项目:PATH将包含sdk二进制文件,标准的autotools变量将使用适当的值定义,CONFIGURE_FLAGS将包含basic ./configure选项来交叉编译autotools项目。它还提供了一些有用的命令。但是请注意,一旦这个脚本执行,环境就只能用于交叉编译,而不再用于本地编译。

8.13.2. Using gdb in Buildroot

Buildroot允许进行交叉调试,调试器在构建机上运行,并与目标机上的gdbserver通信以控制程序的执行。

要实现这一点:

现在,要开始调试一个名为foo的程序,你应该在target上运行:

gdbserver :2345 foo

这将导致gdbserver在TCP端口2345上监听来自交叉gdb的连接。

然后,在主机上使用下面的命令行启动cross gdb:

<buildroot>/output/host/bin/<tuple>-gdb -ix <buildroot>/output/staging/usr/share/buildroot/gdbinit foo

当然,foo必须在当前目录中可用,并使用调试符号构建。通常,你从构建foo的目录启动这个命令(而不是从output/target/启动,因为该目录中的二进制文件被剥离(strip)了)。

<buildroot>/output/staging/usr/share/buildroot/gdbinit文件将告诉交叉gdb在哪里找到目标的库。

最后,从cross gdb连接到目标:

(gdb) target remote <target ip address>:2345

8.13.3. Using ccache in Buildroot

ccache是一个编译器缓存。它存储了每次编译过程产生的目标文件,并且能够通过使用预先存在的目标文件跳过将来对同一个源文件(具有相同的编译器和相同的参数)的编译。当多次从头开始构建几乎相同的版本时,它可以很好地加快构建过程。

Buildroot中集成了对ccache的支持。你只需要在Build options中启用Enable compiler cache。这将自动构建ccache并将其用于每个主机和目标编译。

缓存位于由BR2_CCACHE_DIR配置选项定义的目录中,默认为$HOME/.buildroot-ccache。这个默认位置在Buildroot输出目录之外,因此它可以被不同的Buildroot构建共享。如果您想要删除缓存,只需删除此目录。

你可以通过运行make ccache-stats来获取缓存的统计信息(大小、命中次数、未命中次数等)。

make目标ccache-optionsCCACHE_OPTIONS变量提供了对ccache更通用的访问。例如

# set cache limit size
make CCACHE_OPTIONS="--max-size=5G" ccache-options

# zero statistics counters
make CCACHE_OPTIONS="--zero-stats" ccache-options

ccache对源文件和编译器选项进行哈希。如果编译器选项不同,则不会使用缓存的对象文件。不过,很多编译器选项都包含过渡目录的绝对路径。因此,构建不同的输出目录将导致许多缓存未命中。

为了避免这个问题,buildroot有Use relative paths选项(BR2_CCACHE_USE_BASEDIR)。这将重写所有指向输出目录内的绝对路径为相对路径。因此,更改输出目录不再导致缓存缺失。

相对路径的一个缺点是,它们最终也是目标文件中的相对路径。因此,例如,调试器将不再找到文件,除非你先cd到输出目录。

有关重写绝对路径的更多细节,请参阅ccache手册的“在不同目录中编译”部分

当在Buildroot中使用BR2_CCACHE=y选项启用ccache时:

你可以使用BR2_USE_CCACHE环境变量来覆盖此行为:当设置为1时,ccache的使用是启用的(Buildroot构建期间的默认值),当未设置或设置为与1不同的值时,ccache的使用是禁用的。

8.13.4. Location of downloaded packages

Buildroot下载的各种压缩包都存储在BR2_DL_DIR中,默认情况下是dl目录。如果您想要保留一个完整的Buildroot版本,该版本可以使用相关的压缩包,您可以创建此目录的副本。这将允许您使用完全相同的版本重新生成工具链和目标文件系统。

如果你维护多个Buildroot树,最好有一个共享的下载位置。这可以通过将环境变量BR2_DL_DIR指向一个目录来实现。如果设置了这个值,那么Buildroot配置中的BR2_DL_DIR的值会被覆盖。下面这行代码应该添加到<~/.bashrc>中。

export BR2_DL_DIR=<shared download location>

下载地址也可以通过.config文件中的BR2_DL_DIR选项设置。与.config文件中的大多数选项不同,这个值会被BR2_DL_DIR环境变量覆盖。

8.13.5. Package-specific make targets

运行make <package>将构建并安装特定的包及其依赖项。

对于依赖于Buildroot基础架构的包,有许多特殊的make目标可以独立调用,如下所示:

make <package>-<target>

包构建目标如下(按执行顺序排列):

命令/ 描述
source 获取源代码(下载压缩包、克隆源代码存储库等)
depends 构建并安装构建包所需的所有依赖项
extract 将源代码放在包构建目录中(解压、复制源代码等)
patch 应用补丁(如果有的话)
configure 如果有,请执行configure命令
build 运行编译命令
install-staging target package: 如果需要,在过渡目录中运行包的安装
install-target target package: 如果需要,在目标目录中运行包的安装
install target package: 运行前面的2条安装命令;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. Using Buildroot during development

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文件共存,这意味着:

如果需要不同于这些默认值的位置,可以通过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>-rebuildmake <pkg>-reconfigure目标一起使用。make <pkg>-rebuild all序列会将源代码从<pkg>_OVERRIDE_SRCDIRoutput/build/<package>-custom(多亏了rsync,只复制了修改过的文件),并重新启动这个包的构建过程。

在上面linux包的例子中,开发人员可以更改/home/bob/linux中的源代码,然后运行:

make linux-rebuild all

并且在几秒钟内就可以在output/images中获得更新的Linux内核映像。类似地,可以对/home/bob/busybox中的BusyBox源代码做如下修改:

make busybox-rebuild all

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

Chapter 9. Project-specific customization


对于一个给定的项目,你可能需要执行的典型操作有:

关于此类特定于项目的自定义的重要注意事项:请仔细考虑哪些更改确实是特定于项目的,哪些更改对项目之外的开发人员也有用。Buildroot社区高度推荐并鼓励向官方Buildroot项目上传改进、软件包和董事会支持。当然,有时不可能或不希望上游,因为更改是高度特定或专有的。

本章将介绍如何在Buildroot中针对项目进行定制,以及如何存储这些定制,以便在运行make clean命令后以可重复的方式构建相同的镜像。通过遵循推荐的策略,你甚至可以使用相同的Buildroot树来构建多个不同的项目!

9.1. Recommended directory structure


为项目定制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 files>
|           +-- patches/
|               +-- foo/
|               |   +-- <some patches>
|               +-- 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. Implementing layered customizations

对于一个用户来说,有几个相关的项目在一定程度上需要相同的自定义是很常见的。建议使用分层定制方法,而不是为每个项目重复这些定制,如本节所述。

几乎Buildroot中所有可用的自定义方法,如后构建脚本和根文件系统覆盖,都接受一个以空格分隔的项目列表。指定的项总是按照从左到右的顺序处理。通过创建多个这样的项,一个用于常见的定制,另一个用于真正特定于项目的定制,您可以避免不必要的重复。每一层通常由board/<company>/中的单独目录体现。根据你的项目,你甚至可以引入两个以上的层。

一个用户有两个自定义层common和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"

然后首先应用common层的补丁,然后应用fooboard层的补丁。

9.2. Keeping customizations outside of Buildroot


正如在第9.1节,“推荐的目录结构”中简要提到的那样,您可以将特定于项目的自定义设置放在两个位置:

通过将BR2_EXTERNAL make变量设置为要使用的br2-external树的路径,可以告诉Buildroot使用一棵或多棵br2-external树。它可以传递给任何Buildroot的make调用。它会自动保存在输出目录中隐藏的.br2-external.mk文件中。正因为如此,我们不需要在每次调用make时都传递BR2_EXTERNAL。不过,只要传入一个新值,就可以随时修改它,也可以传入一个空值来删除它。

注意. br2-external树的路径可以是绝对路径,也可以是相对路径。如果传递的是相对路径,请注意,它是相对于主要的Buildroot源目录进行解释的,而不是 Buildroot输出目录。

注意: 如果在Buildroot 2016.11之前使用br2-external树,则需要在Buildroot 2016.11之后使用它之前将其转换。请参阅第27.2节,“迁移到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

我们也可以使用多个br2-external树:

buildroot/ $ make BR2_EXTERNAL=/path/to/foo:/where/we/have/bar menuconfig

或者禁用任何br2-external树:

buildroot/ $ make BR2_EXTERNAL= xconfig

9.2.1. Layout of a br2-external tree

一个br2-external树必须至少包含这3个文件,将在以下章节描述。

除了这些强制文件,br2-external树中可能还存在其他可选内容,如configs/provides/目录。它们也将在接下来的章节中进行描述。

稍后还将描述br2-external树布局的完整示例。

external.desc文件

该文件描述了br2-external树: 该br2-external树的名称和描述。

该文件的格式是以行为单位的,每行以一个关键字开头,然后是一个冒号和一个或多个空格,最后是分配给该关键字的值。目前可以识别的关键字有两个:

名称和相应的BR2_EXTERNAL_$(NAME)_PATH变量的例子:

在下面的示例中,假定名称设置为BAR_42

注意: BR2_EXTERNAL_$(NAME)_PATHBR2_EXTERNAL_$(NAME)_DESC都可以在Kconfig文件和makefile中使用。它们也会在环境中导出,因此可以在post-build、post-image和in-fakeroot脚本中使用。

Config.inexternal.mk文件

这些文件(每个文件可能是空的)可用于定义包配方(即:foo/Config.infoo/foo.mk用于打包在Buildroot本身中的包)或其他自定义配置选项或逻辑。

Buildroot自动包含每个br2-external树中的Config.in,使其出现在顶层配置菜单中,并包含每个br2-external树中的external.mk和其余的makefile逻辑。

它的主要用途是存储包配方。推荐的方法是写一个Config.in文件,看起来像这样:

source "$BR2_EXTERNAL_BAR_42_PATH/package/package1/Config.in"
source "$BR2_EXTERNAL_BAR_42_PATH/package/package2/Config.in"

然后,创建一个如下所示的external.mk文件:

include $(sort $(wildcard $(BR2_EXTERNAL_BAR_42_PATH)/package/*/*.mk))

然后在$(BR2_EXTERNAL_BAR_42_PATH)/package/package1$(BR2_EXTERNAL_BAR_42_PATH)/package/package2中创建正常的Buildroot包,如第18章,向Buildroot添加新包中所述。如果你愿意,也可以将包分组在名为并相应地调整上述路径。

你也可以在Config.in中定义自定义配置选项,在external.mk中定义自定义make逻辑。

configs/目录

你可以将Buildroot的defconfig存储在br2-external树的configs子目录中。Buildroot会自动将它们显示在make list-defconfigs的输出中,并允许它们使用普通的make <name>_defconfig命令加载。它们将在make list-defconfigs输出中可见,位于包含定义它们的br2-external树名称的External configs标签下方。

注意: 如果一个defconfig文件存在于多个br2-external树中,那么将使用最后一个br2-external树中的那个。因此,可以覆盖Buildroot或另一个br2-external树中捆绑的defconfig。

provides/目录

对于某些包,Buildroot提供了在 API 兼容的此类包的两个(或多个)实现之间进行选择。例如,可以选择 libjpeg 或 jpeg-turbo;openssl 和 libressl 之间有一个;可以选择已知的预配置工具链之一……

br2-external可以通过提供一组定义这些选项的文件来扩展这些选项:

自由格式的内容

你可以将所有特定于板的配置文件存储在那里,例如内核配置、根文件系统覆盖层或任何其他Buildroot允许设置位置的配置文件(通过使用BR2_EXTERNAL_$(NAME)_PATH变量)。例如,您可以将路径设置为全局补丁目录、rootfs覆盖层和内核配置文件,如下所示(例如,通过运行make menuconfig并填写这些选项):

BR2_GLOBAL_PATCH_DIR=$(BR2_EXTERNAL_BAR_42_PATH)/patches/
BR2_ROOTFS_OVERLAY=$(BR2_EXTERNAL_BAR_42_PATH)/board/<boardname>/overlay/
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=$(BR2_EXTERNAL_BAR_42_PATH)/board/<boardname>/kernel.config

额外的Linux内核扩展

额外的Linux内核扩展(参见第18.22.2节,“linux-内核-扩展”)可以通过将它们存储在br2-external树的根目录下的linux/目录中来添加。

示例布局

下面是一个使用br2-external所有特性的示例布局(示例内容显示在上面的文件中,与解释br2-external树相关;当然,这完全是为了说明而编造的):

/path/to/br2-ext-tree/
  |- external.desc
  |     |name: BAR_42
  |     |desc: Example br2-external tree
  |     `----
  |
  |- Config.in
  |     |source "$BR2_EXTERNAL_BAR_42_PATH/toolchain/toolchain-external-mine/Config.in.options"
  |     |source "$BR2_EXTERNAL_BAR_42_PATH/package/pkg-1/Config.in"
  |     |source "$BR2_EXTERNAL_BAR_42_PATH/package/pkg-2/Config.in"
  |     |source "$BR2_EXTERNAL_BAR_42_PATH/package/my-jpeg/Config.in"
  |     |
  |     |config BAR_42_FLASH_ADDR
  |     |    hex "my-board flash address"
  |     |    default 0x10AD
  |     `----
  |
  |- external.mk
  |     |include $(sort $(wildcard $(BR2_EXTERNAL_BAR_42_PATH)/package/*/*.mk))
  |     |include $(sort $(wildcard $(BR2_EXTERNAL_BAR_42_PATH)/toolchain/*/*.mk))
  |     |
  |     |flash-my-board:
  |     |    $(BR2_EXTERNAL_BAR_42_PATH)/board/my-board/flash-image \
  |     |        --image $(BINARIES_DIR)/image.bin \
  |     |        --address $(BAR_42_FLASH_ADDR)
  |     `----
  |
  |- package/pkg-1/Config.in
  |     |config BR2_PACKAGE_PKG_1
  |     |    bool "pkg-1"
  |     |    help
  |     |      Some help about pkg-1
  |     `----
  |- package/pkg-1/pkg-1.hash
  |- package/pkg-1/pkg-1.mk
  |     |PKG_1_VERSION = 1.2.3
  |     |PKG_1_SITE = /some/where/to/get/pkg-1
  |     |PKG_1_LICENSE = blabla
  |     |
  |     |define PKG_1_INSTALL_INIT_SYSV
  |     |    $(INSTALL) -D -m 0755 $(PKG_1_PKGDIR)/S99my-daemon \
  |     |                          $(TARGET_DIR)/etc/init.d/S99my-daemon
  |     |endef
  |     |
  |     |$(eval $(autotools-package))
  |     `----
  |- package/pkg-1/S99my-daemon
  |
  |- package/pkg-2/Config.in
  |- package/pkg-2/pkg-2.hash
  |- package/pkg-2/pkg-2.mk
  |
  |- provides/jpeg.in
  |     |config BR2_PACKAGE_MY_JPEG
  |     |    bool "my-jpeg"
  |     `----
  |- package/my-jpeg/Config.in
  |     |config BR2_PACKAGE_PROVIDES_JPEG
  |     |    default "my-jpeg" if BR2_PACKAGE_MY_JPEG
  |     `----
  |- package/my-jpeg/my-jpeg.mk
  |     |# This is a normal package .mk file
  |     |MY_JPEG_VERSION = 1.2.3
  |     |MY_JPEG_SITE = https://example.net/some/place
  |     |MY_JPEG_PROVIDES = jpeg
  |     |$(eval $(autotools-package))
  |     `----
  |
  |- provides/init.in
  |     |config BR2_INIT_MINE
  |     |    bool "my custom init"
  |     |    select BR2_PACKAGE_MY_INIT
  |     |    select BR2_PACKAGE_SKELETON_INIT_MINE if BR2_ROOTFS_SKELETON_DEFAULT
  |     `----
  |
  |- provides/skeleton.in
  |     |config BR2_ROOTFS_SKELETON_MINE
  |     |    bool "my custom skeleton"
  |     |    select BR2_PACKAGE_SKELETON_MINE
  |     `----
  |- package/skeleton-mine/Config.in
  |     |config BR2_PACKAGE_SKELETON_MINE
  |     |    bool
  |     |    select BR2_PACKAGE_HAS_SKELETON
  |     |
  |     |config BR2_PACKAGE_PROVIDES_SKELETON
  |     |    default "skeleton-mine" if BR2_PACKAGE_SKELETON_MINE
  |     `----
  |- package/skeleton-mine/skeleton-mine.mk
  |     |SKELETON_MINE_ADD_TOOLCHAIN_DEPENDENCY = NO
  |     |SKELETON_MINE_ADD_SKELETON_DEPENDENCY = NO
  |     |SKELETON_MINE_PROVIDES = skeleton
  |     |SKELETON_MINE_INSTALL_STAGING = YES
  |     |$(eval $(generic-package))
  |     `----
  |
  |- provides/toolchains.in
  |     |config BR2_TOOLCHAIN_EXTERNAL_MINE
  |     |    bool "my custom toolchain"
  |     |    depends on BR2_some_arch
  |     |    select BR2_INSTALL_LIBSTDCPP
  |     `----
  |- toolchain/toolchain-external-mine/Config.in.options
  |     |if BR2_TOOLCHAIN_EXTERNAL_MINE
  |     |config BR2_TOOLCHAIN_EXTERNAL_PREFIX
  |     |    default "arch-mine-linux-gnu"
  |     |config BR2_PACKAGE_PROVIDES_TOOLCHAIN_EXTERNAL
  |     |    default "toolchain-external-mine"
  |     |endif
  |     `----
  |- toolchain/toolchain-external-mine/toolchain-external-mine.mk
  |     |TOOLCHAIN_EXTERNAL_MINE_SITE = https://example.net/some/place
  |     |TOOLCHAIN_EXTERNAL_MINE_SOURCE = my-toolchain.tar.gz
  |     |$(eval $(toolchain-external-package))
  |     `----
  |
  |- linux/Config.ext.in
  |     |config BR2_LINUX_KERNEL_EXT_EXAMPLE_DRIVER
  |     |    bool "example-external-driver"
  |     |    help
  |     |      Example external driver
  |     |---
  |- linux/linux-ext-example-driver.mk
  |
  |- configs/my-board_defconfig
  |     |BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL_BAR_42_PATH)/patches/"
  |     |BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_BAR_42_PATH)/board/my-board/overlay/"
  |     |BR2_ROOTFS_POST_IMAGE_SCRIPT="$(BR2_EXTERNAL_BAR_42_PATH)/board/my-board/post-image.sh"
  |     |BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_BAR_42_PATH)/board/my-board/kernel.config"
  |     `----
  |
  |- patches/linux/0001-some-change.patch
  |- patches/linux/0002-some-other-change.patch
  |- patches/busybox/0001-fix-something.patch
  |
  |- board/my-board/kernel.config
  |- board/my-board/overlay/var/www/index.html
  |- board/my-board/overlay/var/www/my.css
  |- board/my-board/flash-image
  `- board/my-board/post-image.sh
        |#!/bin/sh
        |generate-my-binary-image \
        |    --root ${BINARIES_DIR}/rootfs.tar \
        |    --kernel ${BINARIES_DIR}/zImage \
        |    --dtb ${BINARIES_DIR}/my-board.dtb \
        |    --output ${BINARIES_DIR}/image.bin
        `----

br2-external树将在menuconfig中可见(扩展布局):

External options  --->
    *** Example br2-external tree (in /path/to/br2-ext-tree/)
    [ ] pkg-1
    [ ] pkg-2
    (0x10AD) my-board flash address

如果您使用不止一棵 br2-external 树,它看起来像(布局展开,第二棵名为FOO_27,但external.desc中没有desc:字段):

External options  --->
    Example br2-external tree  --->
        *** Example br2-external tree (in /path/to/br2-ext-tree)
        [ ] pkg-1
        [ ] pkg-2
        (0x10AD) my-board flash address
    FOO_27  --->
        *** FOO_27 (in /path/to/another-br2-ext)
        [ ] foo
        [ ] bar

此外,jpeg提供程序将在jpeg选项中可见:

Target packages  --->
    Libraries  --->
        Graphics  --->
            [*] jpeg support
                jpeg variant ()  --->
                    ( ) jpeg
                    ( ) jpeg-turbo
                        *** jpeg from: Example br2-external tree ***
                    (X) my-jpeg
                        *** jpeg from: FOO_27 ***
                    ( ) another-jpeg

对于工具链也是类似的:

Toolchain  --->
    Toolchain ()  --->
        ( ) Custom toolchain
            *** Toolchains from: Example br2-external tree ***
        (X) my custom toolchain

注意. toolchain/toolchain-external-mine/Config.in.options中的工具链选项将不会出现在Toolchain菜单中。它们必须在br2-external的顶层Config.in中显式包含,因此将出现在External options菜单中。

9.3. Storing the Buildroot configuration


Buildroot配置可以使用命令make savedefconfig存储。

这将通过删除具有默认值的配置选项来剥离Buildroot配置。结果存储在名为defconfig的文件中。如果你想将其保存在另一个地方,请更改Buildroot配置本身中的BR2_DEFCONFIG选项,或者使用make savedefconfig BR2_DEFCONFIG=<path-to-defconfig>调用make。

推荐存储这个defconfig的地方是configs/<boardname>_defconfig。如果你遵循这个建议,配置将列在make list-defconfigs中,并且可以通过运行make <boardname>_defconfig再次进行设置。

或者,你可以将文件复制到任何其他位置,并使用make defconfig BR2_DEFCONFIG=<path-to-defconfig-file>重新构建。

9.4. Storing the configuration of other components


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提供了一些辅助目标,使配置文件的保存更容易。

9.5. Customizing the generated target filesystem


除了通过make *config更改配置之外,还有一些其他方法可以自定义生成的目标文件系统。

推荐的两种方法是根文件系统覆盖和post构建脚本,这两种方法可以共存。

根文件系统覆盖(BR2_ROOTFS_OVERLAY)

文件系统覆盖层(filesystem overlay)是一个文件树,在目标文件系统构建后直接复制到目标文件系统上。要启用此功能,请将配置选项BR2_ROOTFS_OVERLAY(在System configuration菜单中)设置为覆盖的根目录。你甚至可以指定多个叠加层,以空格分隔。如果你指定了一个相对路径,它将是相对于Buildroot树的根。版本控制系统的隐藏目录,如.git.svn.hg等名为.empty的文件和以~结尾的文件不会被复制。

当启用BR2_ROOTFS_MERGED_USR时,覆盖网不能包含/bin、/lib或/sbin目录,因为Buildroot会将它们创建为指向/usr中相关文件夹的符号链接。在这种情况下,如果覆盖层上有任何程序或库,它们应该放在/usr/bin、/usr/sbin和/usr/lib中

第9.1节,“推荐的目录结构”所示,这个覆盖层的推荐路径是board/<company>/<boardname>/rootfs-overlay

Post-build脚本(BR2_ROOTFS_POST_BUILD_SCRIPT)

Post-build脚本是在Buildroot构建所有选定软件之后,但在组装rootfs映像之前调用的shell脚本。要启用此功能,请在配置选项BR2_ROOTFS_POST_BUILD_SCRIPT(在System configuration菜单中)中指定以空格分隔的post-build脚本列表。如果你指定了一个相对路径,它将是相对于Buildroot树的根。

使用post-build脚本,你可以删除或修改目标文件系统中的任何文件。然而,您应该谨慎使用此功能。每当你发现某个包生成了错误或不需要的文件时,你应该修复该包,而不是使用一些post-build清理脚本来解决它。

第9.1节,“推荐的目录结构”所示,这个脚本的推荐路径是board/<company>/<boardname>/post_build.sh

构建后的脚本以主Buildroot树作为当前工作目录运行。到目标文件系统的路径作为每个脚本的第一个参数传递。如果配置选项BR2_ROOTFS_POST_SCRIPT_ARGS不为空,这些参数也会传递给脚本。所有脚本都将被传递完全相同的参数集,不可能给每个脚本传递不同的参数集。

此外,你也可以使用这些环境变量:

下面将描述另外三种定制目标文件系统的方法,但不推荐使用。

直接修改目标文件系统

对于临时修改,可以直接修改目标文件系统并重新构建映像。目标文件系统位于output/target/目录下。做出更改后,运行make来重建目标文件系统镜像。

这个方法允许你对目标文件系统做任何操作,但是如果你需要使用make clean清理你的Buildroot树,这些更改将会丢失。这种清理在某些情况下是必要的,请参阅8.2节,“了解何时需要完全重建”以了解详情。因此,此解决方案仅适用于快速测试:更改不会在make clean命令中生效。一旦你验证了你的更改,你应该使用根文件系统覆盖层或post-build脚本,确保它们在make clean之后仍然有效。

自定义目标骨架 (BR2_ROOTFS_SKELETON_CUSTOM)

根文件系统映像是根据目标框架创建的,所有包都会在其上安装它们的文件。在构建和安装任何包之前,框架被复制到目标目录output/target。默认的目标框架提供了标准的Unix文件系统布局以及一些基本的init脚本和配置文件。

如果默认的骨架(在system/skeleton下提供)不符合你的需求,你通常会使用根文件系统覆盖或post-build脚本来适应它。然而,如果默认的骨架与您所需要的完全不同,那么使用自定义的骨架可能更合适。

要启用此功能,请启用配置选项BR2_ROOTFS_SKELETON_CUSTOM,并将BR2_ROOTFS_SKELETON_CUSTOM_PATH设置为自定义骨架的路径。这两个选项都可以在System configuration菜单中找到。如果你指定了一个相对路径,它将是相对于Buildroot树的根。

自定义的框架不需要包含/bin、/lib或/sbin目录,因为它们是在构建过程中自动创建的。当启用了BR2_ROOTFS_MERGED_USR时,自定义框架不能包含/bin、/lib或/sbin目录,因为Buildroot会将它们创建为指向/usr中相关文件夹的符号链接。在这种情况下,如果框架中有任何程序或库,它们应该放置在/usr/bin、/usr/sbin和/usr/lib中

不推荐使用这种方法,因为它重复了整个框架,这将导致无法利用在以后的Buildroot版本中对默认框架进行的修复或改进。

Post-fakeroot脚本(BR2_ROOTFS_POST_FAKEROOT_SCRIPT)

当聚合最终图像时,过程的某些部分需要root权限:在/dev中创建设备节点,设置文件和目录的权限或所有权……为了避免需要实际的root权限,Buildroot使用fakeroot来模拟root权限。这并不是真正成为root的完全替代,但足以满足Buildroot的需求。

Post-fakeroot脚本是在fakeroot阶段结束时调用的shell脚本,就在调用文件系统映像生成器之前。因此,它们在fakeroot上下文中被调用。

Post-fakeroot脚本在需要调整文件系统以进行通常只有root用户才能使用的修改时非常有用。

注意: 建议使用现有的机制来设置文件权限或在/dev中创建条目(参见9.5.1节,“设置文件权限和所有权并添加自定义设备节点”)或创建用户(参见9.6节,“添加自定义用户账户”)

注意: post-build脚本(上面)和fakeroot脚本之间的区别在于,post-build脚本不会在fakeroot上下文中调用。

注意: 使用fakeroot并不能绝对替代真正的root。fakeroot只会伪造文件访问权限和类型(普通、块设备或字符设备……)以及uid/gid;这些是在内存中模拟的。

9.5.1. Setting file permissions and ownership and adding custom devices nodes

有时需要对文件或设备节点设置特定的权限或所有权。例如,某些文件可能需要由root拥有。因为post-build脚本不是以root身份运行的,所以除非在post-build脚本中显式使用fakeroot,否则无法从root进行更改。

相反,Buildroot提供了对所谓权限表的支持。要使用此功能,请将配置选项BR2_ROOTFS_DEVICE_TABLE设置为空格分隔的权限表列表,即遵循makedev语法的普通文本文件。

如果你使用的是静态设备表(即不使用devtmpfsmdev(e)udev),那么你可以使用相同的语法在所谓的设备表中添加设备节点。要使用此功能,请将配置选项BR2_ROOTFS_STATIC_DEVICE_TABLE设置为一个由空格分隔的设备表列表。

第9.1节,“推荐的目录结构”所示,此类文件的推荐位置是board/<company>/<boardname>/

需要注意的是,如果特定的权限或设备节点与特定的应用程序相关,则应该在包的.mk文件中设置变量FOO_PERMISSIONSFOO_DEVICES(参见第18.6.2节,“generic-package引用”)。

9.6. Adding custom user accounts


有时需要在目标系统中添加特定用户。为了满足这个需求,Buildroot提供了对所谓用户表的支持。要使用此功能,请将配置选项BR2_ROOTFS_USERS_TABLES设置为空格分隔的用户表列表,遵循makeusers语法的普通文本文件。

第9.1节,“推荐的目录结构”所示,此类文件的推荐位置是board/<company>/<boardname>/

需要注意的是,如果自定义用户与特定应用程序相关,则应该在包的.mk文件中设置变量FOO_USERS(参见第18.6.2节,“generic-package引用”)。

9.7. Customization after the images have been created


虽然在构建文件系统映像、内核和引导装载器之前运行post-build脚本(9.5节,“自定义生成的目标文件系统”),但在创建所有映像之后,可以使用post-image scripts执行一些特定的操作。

例如,Post-image脚本可以用于在NFS服务器导出的位置自动提取根文件系统的压缩包,或者创建一个捆绑根文件系统和内核映像的特殊固件映像,或者用于项目所需的任何其他自定义操作。

要启用此功能,请在配置选项BR2_ROOTFS_POST_IMAGE_SCRIPT(在System configuration菜单中)中指定空格分隔的post-image脚本列表。如果你指定了一个相对路径,它将是相对于Buildroot树的根。

就像post-build脚本一样,post-image脚本也以主Buildroot树作为当前工作目录运行。images输出目录的路径作为第一个参数传递给每个脚本。如果配置选项BR2_ROOTFS_POST_SCRIPT_ARGS不为空,这些参数也会传递给脚本。所有脚本都将被传递完全相同的参数集,不可能给每个脚本传递不同的参数集。

同样,就像构建后的脚本一样,这些脚本可以访问环境变量BR2_CONFIG, HOST_DIR, STAGING_DIR, TARGET_DIR, BUILD_DIR, BINARIES_DIR, CONFIG_DIRBASE_DIR

post-image脚本将作为执行Buildroot的用户执行,该用户通常不应该是root用户。因此,在这些脚本中任何需要root权限的操作都需要特殊处理(使用fakeroot或sudo),这留给脚本开发人员。

9.8. Adding project-specific patches


有时,在Buildroot中提供的包之上,为包应用额外的补丁是有用的。例如,这可能用于支持项目中的自定义功能,或者在开发新架构时。

BR2_GLOBAL_PATCH_DIR配置选项可用于指定由空格分隔的一个或多个包含包补丁的目录列表。

对于特定包<packageversion>的特定版本<packagename>,补丁从BR2_GLOBAL_PATCH_DIR应用如下:

  1. 对于BR2_GLOBAL_PATCH_DIR中存在的每个目录<global-patch-dir><package-patch-dir>将被确定如下:

    • <global-patch-dir>/<packagename>/<packageversion>/ 如果该目录存在。
    • 否则, <global-patch-dir>/<packagename> 如果该目录存在。
  2. 然后,补丁将从<package-patch-dir>中应用,如下所示:

    • 如果包目录中存在series文件,则根据series文件应用补丁;
    • 否则,补丁文件匹配*.patch按字母顺序应用。因此,为了确保它们按照正确的顺序应用,强烈建议将补丁文件命名为:<number>-<description>.patch,其中<number>为应用顺序。

有关如何为包应用补丁的信息,请参阅19.2节,“如何应用补丁”

BR2_GLOBAL_PATCH_DIR选项是为包指定自定义补丁目录的首选方法。它可以用来为buildroot中的任何包指定补丁目录。它还应该用来代替U-Boot和Barebox等包的自定义补丁目录选项。通过这样做,它将允许用户从一个顶层目录管理他们的补丁。

BR2_GLOBAL_PATCH_DIR是指定自定义补丁的首选方法,但例外情况是BR2_LINUX_KERNEL_PATCH。应该使用BR2_LINUX_KERNEL_PATCH来指定URL中可用的内核补丁。注意: BR2_LINUX_KERNEL_PATCH指定在BR2_GLOBAL_PATCH_DIR中可用补丁之后应用的内核补丁,因为它是通过Linux包的后补丁钩子完成的。

9.9. Adding project-specific packages


一般来说,任何新的包都应该直接添加到package目录中,并提交到Buildroot上游项目。如何向Buildroot中添加包在第18章,向Buildroot添加新包中有详细说明,这里不再赘述。然而,您的项目可能需要一些无法上传到的专有包。本节将解释如何将此类特定于项目的包保存在特定于项目的目录中。

9.1节“推荐的目录结构”所示,项目特定包的推荐位置是package/<company>/。如果你正在使用br2-external tree特性(参见9.2节,在Buildroot之外保留自定义项),建议将它们放在br2-external tree中名为package/的子目录中。

但是,除非我们执行一些额外的步骤,否则Buildroot不会知道这个位置中的包。正如在第18章,向Buildroot添加新包中所解释的那样,Buildroot中的包基本上由两个文件组成:一个.mk文件(描述如何构建包)和一个Config.in文件(描述此包的配置选项)。

Buildroot将自动将mk文件包含在package目录的一级子目录中(使用package/*/*.mk模式)。如果我们想让Buildroot包含更深层次的子目录中的.mk文件(比如package/<company>/package1/),那么我们只需要在包含这些额外的.mk文件的一级子目录中添加一个.mk文件。因此,创建一个文件package/<company>/<company>.mk,包含以下内容(假设在package/<company>/下面只有一个额外的目录层):

include $(sort $(wildcard package/<company>/*/*.mk))

对于Config.in文件,创建一个文件package/<company>/Config.in,其中包含你所有包的Config.in文件。由于kconfig的source命令不支持通配符,因此必须提供一个详尽的列表。例如:

source "package/<company>/package1/Config.in"
source "package/<company>/package2/Config.in"

package/Config.in中包含这个新文件package/<company>/Config.in,最好是在公司特定的菜单中,以便更容易与未来的Buildroot版本合并。

如果使用br2-external树,请参考9.2节,“在Buildroot之外保留自定义”来了解如何填充这些文件。

9.10. Quick guide to storing your project-specific customizations


本章前面描述了针对项目进行定制的不同方法。本节将通过分步说明如何存储特定于项目的自定义来总结所有这些内容。显然,与项目无关的步骤可以跳过。

  1. make menuconfig来配置工具链、包和内核。
  2. make linux-menuconfig来更新内核配置,类似于busybox、uclibc…
  3. mkdir -p board/<manufacturer>/<boardname>
  4. 将以下选项设置为board/<manufacturer>/<boardname>/<package>.config(只要它们是相关的):

    • BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE
    • BR2_PACKAGE_BUSYBOX_CONFIG
    • BR2_UCLIBC_CONFIG
    • BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_CONFIG_FILE
    • BR2_TARGET_BAREBOX_CUSTOM_CONFIG_FILE
    • BR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE
  5. 写入配置文件:

    • make linux-update-defconfig
    • make busybox-update-config
    • make uclibc-update-config
    • cp <output>/build/at91bootstrap3-*/.config board/<manufacturer>/<boardname>/at91bootstrap3.config
    • make barebox-update-defconfig
    • make uboot-update-defconfig
  6. 创建board/<manufacturer>/<boardname>/rootfs-overlay/,并用你需要的其他文件填充它。board/<manufacturer>/<boardname>/rootfs-overlay/etc/inittab。设置BR2_ROOTFS_OVERLAYboard/<manufacturer>/<boardname>/rootfs-overlay
  7. 创建一个post-build脚本board/<manufacturer>/<boardname>/post_build.sh。设置BR2_ROOTFS_POST_BUILD_SCRIPTboard/<manufacturer>/<boardname>/post_build.sh
  8. 如果需要设置额外的setuid权限或创建设备节点,请创建board/<manufacturer>/<boardname>/device_table.txt,并将该路径添加到BR2_ROOTFS_DEVICE_TABLE
  9. 如果必须创建其他用户帐户,请创建board/<manufacturer>/<boardname>/users_table.txt,并将该路径添加到BR2_ROOTFS_USERS_TABLES
  10. 要向某些包添加自定义补丁,请将BR2_GLOBAL_PATCH_DIR设置为board/<manufacturer>/<boardname>/patches/,并在以包命名的子目录中为每个包添加补丁。每个补丁应该被命名为<packagename>-<num>-<description>.patch
  11. 特别是对于Linux内核,还存在BR2_LINUX_KERNEL_PATCH选项,其主要优点是它也可以从URL下载补丁。如果你不需要它,BR2_GLOBAL_PATCH_DIR是首选。U-Boot、Barebox、at91bootstrap和at91bootstrap3也有单独的选项,但它们并没有比BR2_GLOBAL_PATCH_DIR提供任何优势,未来可能会被删除。
  12. 如果您需要添加项目特定的包,请创建package/<manufacturer>/并将您的包放在该目录中。创建一个完整的<manufacturer>.mk文件,其中包含你所有包的.mk文件。创建一个完整的Config.in文件,它是所有包的Config.in文件的源文件。从Buildroot的package/Config.in文件中包含这个Config.in文件。
  13. make savedefconfig 来保存buildroot配置。
  14. cp defconfig configs/<boardname>_defconfig

Chapter 10. Integration topics


本章将讨论各种事物如何在系统级进行集成。Buildroot是高度可配置的,这里讨论的几乎所有内容都可以通过rootfs覆盖或自定义骨架配置更改或覆盖。

10.1. Systemd


本章描述Buildroot集成systemd时所做的决策,以及如何实现各种用例。

10.1.1. DBus daemon

Systemd需要一个DBus守护进程。它有两种选择:传统的dbus (BR2_PACKAGE_DBUS)和bus1 dbus-broker (BR2_PACKAGE_DBUS_BROKER)。必须至少选择其中一个。如果两者都包含在配置中,则dbus-broker将用作系统总线,但是传统的dbus-daemon仍然被安装,并且可以用作会话总线。还有它的工具(例如:dbus-send)可以使用(systemd本身有busctl作为替代)。此外,传统的dbus包是唯一提供libdbus的包,它被许多包用作dbus集成库。

在dbus和dbus-broker情况下,守护进程都作为用户dbus运行。两者的DBus配置文件也是相同的。

为了确保只有两个守护进程中的一个作为系统总线启动,当选择dbus-broker时,dbus包的systemd激活文件(dbus.socketmulti-user.target.wants中的dbus.service符号链接)被删除。

10.2. Using SELinux in Buildroot


SELinux是一个强制访问控制策略的Linux内核安全模块。除了传统的文件权限和访问控制列表之外,SELinux还允许为用户或进程编写规则,以访问特定的资源(文件、套接字等)。

SELinux有三种操作模式。

在Buildroot中,操作模式由BR2_PACKAGE_REFPOLICY_POLICY_STATE_*配置选项控制。Linux内核也有各种影响SELinux如何启用的配置选项(请参阅Linux内核源代码中的security/selinux/Kconfig)。

在Buildroot中,默认情况下,SELinux策略由上游refpolicy项目提供,通过BR2_PACKAGE_REFPOLICY启用。

10.2.1. Enabling SELinux support

要在Buildroot生成的系统中正确支持SELinux,必须启用以下配置选项:

此外,文件系统映像格式必须支持扩展属性。

10.2.2. SELinux policy tweaking

SELinux refpolicy包含可以在构建时启用或禁用的模块。每个模块都提供了许多SELinux规则。在Buildroot中,非基础模块默认是禁用的,并且提供了几种启用这些模块的方法:

Buildroot还允许完全覆盖refpolicy。这允许提供专门为给定系统设计的完全自定义策略。这样做时,上述所有机制都被禁用:没有额外的SElinux模块添加到策略中,自定义策略中所有可用的模块都被启用并内置到最终的二进制策略中。自定义策略必须是官方refpolicy的分支。

为了完全覆盖refpolicy,必须设置以下配置变量:

Chapter 11. Frequently Asked Questions & Troubleshooting


11.1. The boot hangs after Starting network…


如果启动过程在以下消息之后挂起(消息不一定完全相似,取决于选择的包列表):

Freeing init memory: 3972K
Initializing random number generator... done.
Starting network...
Starting dropbear sshd: generating rsa key... generating dsa key... OK

这意味着系统正在运行,但没有在串行控制台中启动shell。为了让系统在串行控制台启动shell,你必须进入Buildroot配置,在System configuration中,修改Run a getty (login prompt) after boot,并在getty options子菜单中设置适当的端口和波特率。这将自动调整生成的系统的/etc/inittab文件,以便shell在正确的串口上启动。

11.2. Why is there no compiler on the target?


已经决定从Buildroot-2012.11版本开始停止对目标上原生编译器的支持,原因如下:

如果无论如何你都需要在目标上安装编译器,那么Buildroot不适合你。在这种情况下,你需要一个真正的分布,你应该选择这样的分布:

11.3. Why are there no development files on the target?


由于目标上没有可用的编译器(参见11.2节,“为什么目标上没有编译器?”),因此浪费头文件或静态库的空间是没有意义的。

因此,自Buildroot-2012.11发布以来,这些文件总是从target中删除。

11.4. Why is there no documentation on the target?


因为Buildroot主要针对机载资源有限(CPU、ram、大容量存储)的小型或非常小的目标硬件,所以浪费文档数据的空间是没有意义的。

如果无论如何你都需要目标上的文档数据,那么Buildroot并不适合你,你应该寻找一个真正的发行版(参见:11.2节,“为什么目标上没有编译器?”)。

11.5. Why are some packages not visible in the Buildroot config menu?


如果一个包存在于Buildroot树中,但没有出现在config菜单中,这很可能意味着包的某些依赖项没有得到满足。

要了解有关包依赖项的更多信息,请在配置菜单中搜索包符号(参见8.1节, “make tips”)。

然后,您可能必须递归地启用几个选项(对应于未满足的依赖项),以最终能够选择包。

如果由于一些未满足的工具链选项导致包不可见,那么您当然应该运行完全重建(请参阅8.1节, “make tips”了解更多解释)。

11.6. Why not use the target directory as a chroot directory?


有很多理由使用目标目录而不是chroot目录,其中包括:

由于这些原因,使用目标目录作为新的根目录运行chroot的命令很可能会失败。

如果你想在chroot目录下运行目标文件系统,或者作为NFS的根目录,那么使用在images/目录下生成的压缩镜像并将其提取为根目录。

11.7. Why doesn’t Buildroot generate binary packages (.deb, .ipkg…)?


在Buildroot列表中经常讨论的一个特性是“包管理”。总而言之,我们的想法是添加一些跟踪哪个Buildroot包安装了什么文件,目标是:

一般来说,大多数人认为这很容易做到:只需跟踪哪个软件包安装了什么,并在软件包未选中时删除它。然而,实际情况要复杂得多:

基于所有这些原因,结论是,添加已安装文件的跟踪以在包未选中时删除它们,或生成二进制包的存储库,是一件非常难以可靠实现的事情,并且会增加很多复杂性。

关于这个问题,Buildroot的开发者发表了如下立场声明:

11.8. How to speed-up the build process?


由于Buildroot通常需要对整个系统进行完整的重构,这可能会很长时间,因此我们提供了以下一些提示来帮助减少构建时间:

Chapter 12. Known issues


Chapter 13. Legal notice and licensing


13.1. Complying with open source licenses


Buildroot的所有最终产品(工具链、根文件系统、内核、引导装载器)都包含在各种许可证下发布的开源软件。

使用开源软件使您可以自由地构建丰富的嵌入式系统,从广泛的软件包中进行选择,但也强加了一些您必须了解和尊重的义务。有些许可要求您在产品文档中发布许可文本。另一些则要求你将软件的源代码重新分发给那些接收你的产品的人。

每个许可证的确切要求都记录在每个软件包中,遵守这些要求是你的责任(或你的法律办公室的责任)。为了让你更容易,Buildroot可以为你收集一些你可能需要的材料。要生成这个材料,在你用make menuconfig, make xconfigmake gconfig配置好Buildroot后,运行:

make legal-info

Buildroot将在输出目录的legal-info/子目录下收集与法律相关的材料。你会发现:

请注意,Buildroot的legal-info功能的目的是生成与软件包许可的法律合规性有关的所有材料。Buildroot不会尝试生产你必须以某种方式公开的确切材料。当然,生产的材料比严格遵守法律所需要的要多。例如,它为在类似bsd许可证下发布的包生成源代码,您不需要以源代码形式重新发布。

此外,由于技术上的限制,Buildroot不会生成您将要或可能需要的一些材料,例如一些外部工具链的工具链源代码和Buildroot源代码本身。当你运行make legal-info时,Buildroot会在README文件中产生警告,通知你相关材料无法保存。

最后,请记住,make legal-info的输出是基于每个包秘诀中的声明式语句。Buildroot的开发人员会尽他们所能保持这些声明式语句尽可能准确。然而,很有可能这些声明性语句并不都是完全准确或详尽的。你(或你的法律部门)必须在使用make legal-info作为自己的合规交付之前检查它的输出。参见Buildroot发行版根目录的COPYING文件中的NO WARRANTY条款(第11条和第12条)。

13.2. Complying with the Buildroot license


Buildroot本身是一个开源软件,在GNU通用公共许可证,版本2或(根据您的选择)任何更新版本下发布,但下面详细介绍的包补丁除外。然而,作为一个构建系统,它通常不是最终产品的一部分:如果您为一个设备开发了根文件系统、内核、引导装载程序或工具链,那么Buildroot的代码只存在于开发机器上,而不在设备存储中。

尽管如此,Buildroot开发人员的普遍看法是,在发布包含gpl许可软件的产品时,应该将Buildroot源代码连同其他包的源代码一起发布。这是因为GNU GPL将可执行作品的“完整源代码”定义为“它包含的所有模块的所有源代码,加上任何相关的接口定义文件,加上用于控制可执行作品的编译和安装的脚本”。Buildroot是用于控制可执行文件的编译和安装的脚本的一部分,因此它被认为是必须重新发布的材料的一部分。

请记住,这只是Buildroot开发人员的意见,如果有任何疑问,请咨询法律部门或律师。

13.2.1. Patches to packages

Buildroot还打包了补丁文件,这些文件应用于各种包的源代码。这些补丁没有被Buildroot的许可证覆盖。相反,它们被应用补丁的软件的许可证所覆盖。当上述软件在多个许可证下可用时,Buildroot补丁只在公开可访问许可证下提供。

请参阅第19章为包打补丁以了解技术细节。

Chapter 14. Beyond Buildroot


14.1. Boot the generated images


14.1.1. NFS boot

要实现NFS-boot,请在文件系统映像菜单中启用tar根文件系统。

在完成构建后,只需运行以下命令设置NFS-root目录:

sudo tar -xavf /path/to/output_dir/rootfs.tar -C /path/to/nfs_root_dir

记得把这个路径添加到/etc/exports中。

然后,您可以从您的目标执行NFS-boot。

14.1.2. Live CD

要构建一个实时CD镜像,请启用文件系统镜像菜单中的iso镜像选项。注意,这个选项只在x86和x86-64架构上可用,并且如果您使用Buildroot构建内核的话。

您可以使用IsoLinux、Grub或Grub 2构建一个活动CD映像作为引导程序,但只有IsoLinux支持使该映像既可用作活动CD,也可用作活动USB(通过构建混合映像选项)。

你可以使用QEMU测试你的live CD镜像:

qemu-system-i386 -cdrom output/images/rootfs.iso9660

如果它是一个混合ISO,也可以将其用作硬盘镜像:

qemu-system-i386 -hda output/images/rootfs.iso9660

可以使用dd轻松地将其闪存到USB驱动器:

dd if=output/images/rootfs.iso9660 of=/dev/sdb

14.2. Chroot


如果你想在生成的图像中使用chroot,那么有几件事你应该知道:

Part III. Developer guide

Chapter 15. How Buildroot works


如上所述,Buildroot基本上是一组makefile,使用正确的选项下载、配置和编译软件。它还包括各种软件包的补丁——主要涉及交叉编译工具链(gccbinutilsuClibc)。

基本上每个软件包都有一个Makefile,它们以.mk扩展名命名。makefile分为许多不同的部分。

每个目录至少包含2个文件:

主Makefile执行以下步骤(在配置完成后):

Chapter 16. Coding style


总的来说,这些编码样式规则是为了帮助您在Buildroot中添加新文件或重构现有文件。

如果你稍微修改了一些现有的文件,重要的是要保持整个文件的一致性,这样你就可以:

16.1. Config.in file


Config.in文件包含了几乎所有Buildroot中可配置的条目。

条目具有以下模式:

config BR2_PACKAGE_LIBFOO
        bool "libfoo"
        depends on BR2_PACKAGE_LIBBAZ
        select BR2_PACKAGE_LIBBAR
        help
          This is a comment that explains what libfoo is. The help text
          should be wrapped.

          http://foosoftware.org/libfoo/

Config.in文件是Buildroot中使用的配置工具的输入,它是常规的Kconfig。有关Kconfig语言的更多细节,请参阅http://kernel.org/doc/Documentation/kbuild/kconfig-language.txt

16.2. The .mk file


################################################################################
#
# libfoo
#
################################################################################
LIBFOO_VERSION = 1.0
LIBFOO_CONF_OPTS += --without-python-support

不要对齐=符号。

define LIBFOO_REMOVE_DOC
        $(RM) -r $(TARGET_DIR)/usr/share/libfoo/doc \
                $(TARGET_DIR)/usr/share/man/man3/libfoo*
endef
请注意,`define`块中的命令总是以tab开头,因此make将它们识别为命令。

YES:

ifeq ($(BR2_PACKAGE_PYTHON3),y)
LIBFOO_CONF_OPTS += --with-python-support
LIBFOO_DEPENDENCIES += python3
else
LIBFOO_CONF_OPTS += --without-python-support
endif

NO:

LIBFOO_CONF_OPTS += --with$(if $(BR2_PACKAGE_PYTHON3),,out)-python-support
LIBFOO_DEPENDENCIES += $(if $(BR2_PACKAGE_PYTHON3),python3,)
*   保持配置选项和依赖项接近。

YES:

ifneq ($(BR2_LIBFOO_INSTALL_DATA),y)
define LIBFOO_REMOVE_DATA
        $(RM) -r $(TARGET_DIR)/usr/share/libfoo/data
endef
LIBFOO_POST_INSTALL_TARGET_HOOKS += LIBFOO_REMOVE_DATA
endif

NO:

define LIBFOO_REMOVE_DATA
        $(RM) -r $(TARGET_DIR)/usr/share/libfoo/data
endef

ifneq ($(BR2_LIBFOO_INSTALL_DATA),y)
LIBFOO_POST_INSTALL_TARGET_HOOKS += LIBFOO_REMOVE_DATA
endif

16.3. The genimage.cfg file


genimage.cfg文件包含了输出图像布局,genimage工具用来创建最终的.img文件。

下面是一个例子:

image efi-part.vfat {
        vfat {
                file EFI {
                        image = "efi-part/EFI"
                }

                file Image {
                        image = "Image"
                }
        }

        size = 32M
}

image sdimage.img {
        hdimage {
        }

        partition u-boot {
                image = "efi-part.vfat"
                offset = 8K
        }

        partition root {
                image = "rootfs.ext2"
                size = 512M
        }
}

genimage.cfg文件是 Buildroot 中使用的 genimage 工具的输入,用于生成最终映像文件(即 sdcard.img)。有关genimage语言的更多详细信息,请参阅https://github.com/pengutronix/genimage/blob/master/README.rst

16.4. The documentation


文档使用asciidoc格式。

有关asciidoc语法的更多细节,请参阅 https://asciidoc-py.github.io/userguide.html.

16.5. Support scripts


support/utils/目录中的一些脚本是用Python编写的,并且应该遵循PEP8 Python代码风格指南

Chapter 17. Adding support for a particular board


Buildroot包含几个公开可用的硬件板的基本配置,以便使用这种板的用户可以轻松构建已知可以工作的系统。您也欢迎添加对其他板的支持到Buildroot。

为此,您需要创建一个普通的Buildroot配置,为硬件构建一个基本的系统:(内部)工具链、内核、引导装载程序、文件系统和一个简单的仅busybox的用户空间。不应该选择任何特定的包:配置应该尽可能少,并且只应该为目标平台构建一个可工作的基本BusyBox系统。当然,你可以在内部项目中使用更复杂的配置,但Buildroot项目只会集成基本的电路板配置。这是因为包的选择是高度特定于应用程序的。

一旦你有了一个可以运行的配置,运行make savedefconfig。这将在Buildroot源码树的根目录中生成一个最小的defconfig文件。将这个文件移动到configs/目录,并将其重命名为<boardname>_defconfig。如果配置有点复杂,最好手动重新格式化并将其分成多个部分,在每个部分之前添加注释。典型的部分是体系结构、工具链选项(通常只是linux头文件版本)、固件、引导装载程序、内核和文件系统。

始终使用固定版本或提交不同组件的哈希值,而不是“最新”版本。例如,设置BR2_LINUX_KERNEL_CUSTOM_VERSION=yBR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE为你测试的内核版本。

建议使用尽可能多的上游版本的Linux内核和引导装载程序,并使用尽可能多的默认内核和引导装载程序配置。如果它们对你的板来说是不正确的,或者不存在默认值,我们鼓励你将修复发送到相应的上游项目。

然而,与此同时,您可能希望存储特定于目标平台的内核或引导装载程序配置或补丁。为此,创建一个目录board/<manufacturer>和一个子目录board/<manufacturer>/<boardname>。然后,您可以将补丁和配置存储在这些目录中,并从主Buildroot配置中引用它们。更多细节请参考第9章,项目定制

在为新开发板提交补丁之前,建议使用最新的gitlab-CI docker容器进行构建,以测试它。要做到这一点,请使用utils/docker-run脚本,并在其中执行以下命令:

$ make <boardname>_defconfig
$ make

默认情况下,Buildroot开发人员使用托管在gitlab.com registry上的官方镜像,它应该对大多数使用都很方便。如果你仍然想构建自己的docker镜像,可以在官方镜像的基础上使用自己的Dockerfile的FROM指令构建:

FROM registry.gitlab.com/buildroot.org/buildroot/base:YYYYMMDD.HHMM
RUN ...
COPY ...

当前版本号YYYYMMDD.HHMM可以在Buildroot源代码树顶部的.gitlab-ci.yml文件中找到;所有过去的版本也都列在上述注册表中。

Chapter 18. Adding new packages to Buildroot


本节介绍如何将新的包(用户空间库或应用程序)集成到Buildroot中。它还展示了如何集成现有的包,这是修复问题或调整配置所需的。

当你添加一个新包时,请确保在各种条件下测试它(参见18.25.3节,“如何测试你的包”),并检查它的编码风格(参见18.25.2节,“如何检查编码风格”)。

18.1. Package directory


首先,在package目录下为你的软件创建一个目录,例如libfoo

有些包按主题分组在子目录中:x11r7qt5gstreamer。如果您的包适合这些类别之一,则在这些类别中创建您的包目录。但是,不鼓励创建新的子目录。

18.2. Config files


要在配置工具中显示包,您需要在包目录中创建配置文件。有两种类型:Config.inConfig.in.host

18.2.1. Config.in file

对于在目标上使用的包,创建一个名为Config.in的文件。该文件将包含与我们的libfoo软件相关的选项描述,它们将被使用并显示在配置工具中。它基本上应该包含:

config BR2_PACKAGE_LIBFOO
        bool "libfoo"
        help
          This is a comment that explains what libfoo is. The help text
          should be wrapped.

          http://foosoftware.org/libfoo/

bool行、help行和其他关于配置选项的元数据信息必须用一个tab缩进。帮助文本本身应该用一个制表符和两个空格缩进,每行应该换行以容纳72列,其中制表符为8,因此文本本身有62个字符。帮助文本必须在空行之后提到项目的上游URL。

作为Buildroot特有的约定,属性的顺序如下:

  1. 选项的类型:boolstring
  2. 如果需要,default
  3. depends on表单中,任何依赖于目标的内容
  4. 任何依赖depends on形式的工具链
  5. 任何依赖于depends on形式的其他包的依赖
  6. select表单的任何依赖项
  7. 帮助关键字和帮助文本。

你可以在if BR2_PACKAGE_LIBFOO…endif语句中添加其他子选项来配置软件中的特定内容。您可以在其他包中查看示例。Config.in文件的语法与内核Kconfig文件的语法相同。这种语法的文档可以在http://kernel.org/doc/Documentation/kbuild/kconfig-language.txt找到。

最后,你必须将新的libfoo/Config.in添加到package/Config.in中(或者如果你决定将包放在现有类别中的某个类别子目录中)。其中包含的文件按每个类别的字母顺序排序,不应该包含除包的裸名之外的任何内容。

source "package/libfoo/Config.in"

18.2.2. Config.in.host file

还需要为主机系统构建一些包。这里有两种选择。

config BR2_PACKAGE_HOST_FOO
        bool "host foo"
        help
          This is a comment that explains what foo for the host is.

          http://foosoftware.org/foo/

Config.in文件相同的编码风格和选项是有效的。

最后,你必须将新的libfoo/Config.in.host添加到package/Config.in.host中。其中包含的文件按字母顺序排序,除了包的裸名之外不应该包含任何内容。

source "package/foo/Config.in.host"

然后可以从Host utilities菜单中获取host包。

18.2.3. Choosing depends on or select

包的Config.in文件也必须确保启用了依赖项。通常,Buildroot使用以下规则。

注意. kconfig语言当前的问题是,这两个依赖关系语义在内部没有链接。因此,可以选择一个包,它的一个依赖项/需求没有得到满足。

一个例子说明了selectdepends on的用法。

config BR2_PACKAGE_RRDTOOL
        bool "rrdtool"
        depends on BR2_USE_WCHAR
        select BR2_PACKAGE_FREETYPE
        select BR2_PACKAGE_LIBART
        select BR2_PACKAGE_LIBPNG
        select BR2_PACKAGE_ZLIB
        help
          RRDtool is the OpenSource industry standard, high performance
          data logging and graphing system for time series data.

          http://oss.oetiker.ch/rrdtool/

comment "rrdtool needs a toolchain w/ wchar"
        depends on !BR2_USE_WCHAR

请注意,这两种依赖关系仅在同类依赖关系下才具有传递性。

这意味着,在下面的例子中:

config BR2_PACKAGE_A
        bool "Package A"

config BR2_PACKAGE_B
        bool "Package B"
        depends on BR2_PACKAGE_A

config BR2_PACKAGE_C
        bool "Package C"
        depends on BR2_PACKAGE_B

config BR2_PACKAGE_D
        bool "Package D"
        select BR2_PACKAGE_B

config BR2_PACKAGE_E
        bool "Package E"
        select BR2_PACKAGE_D
config BR2_PACKAGE_D
        bool "Package D"
        depends on BR2_PACKAGE_A
        select BR2_PACKAGE_B

config BR2_PACKAGE_E
        bool "Package E"
        depends on BR2_PACKAGE_A
        select BR2_PACKAGE_D

总的来说,对于包库依赖项,select应该是首选。

请注意,这样的依赖项将确保依赖项选项也启用,但不一定在包之前构建。为此,依赖关系还需要在包的.mk文件中表达。

进一步的格式化细节:参见编码风格

18.2.4. Dependencies on target and toolchain options

许多包依赖于工具链的某些选项:C库的选择、C++支持、线程支持、RPC支持、wchar支持或动态库支持。有些包只能在某些目标体系结构上构建,或者在处理器中有MMU可用的情况下构建。

这些依赖关系必须在Config.in文件中使用适当的depends on语句来表示。此外,对于工具链选项的依赖项,当选项未启用时,应显示一个comment,以便用户知道为什么该包不可用。不应该在注释中显示对目标体系结构或MMU支持的依赖关系:因为用户不太可能自由选择另一个目标,所以显式显示这些依赖关系没有什么意义。

只有在满足工具链选项依赖关系时,config选项本身可见时,comment才应该可见。这意味着该包的所有其他依赖项(包括对目标架构和MMU支持的依赖项)必须在comment定义上重复。为了清楚起见,这些非工具链选项的depends on语句应该与工具链选项的depends on语句保持分离。如果在同一个文件(通常是主包)中有配置项依赖,最好使用全局的if…endif结构,而不是在注释和其他配置项上重复depends on语句。

foo包的依赖comment的一般格式是:

foo needs a toolchain w/ featA, featB, featC

例如:

mpd needs a toolchain w/ C++, threads, wchar

crda needs a toolchain w/ threads

请注意,这段文本故意保持简短,以适应80个字符的终端。

本节的其余部分将列举不同的target和工具链选项、要依赖的相应配置符号以及注释中要使用的文本。

18.2.5. Dependencies on a Linux kernel built by buildroot

有些包需要由buildroot构建Linux内核。这些通常是内核模块或固件。应该在配置文件中添加注释。在文件中表达这种依赖关系,类似于工具链选项上的依赖关系。一般格式如下:

foo needs a Linux kernel to be built

如果同时依赖工具链选项和Linux内核,请使用以下格式:

foo needs a toolchain w/ featA, featB, featC and a Linux kernel to be built

18.2.6. Dependencies on udev /dev management

如果一个包需要udev /dev管理,它应该依赖符号BR2_PACKAGE_HAS_UDEV,并且应该添加以下注释:

foo needs udev /dev management

如果对工具链选项和udev /dev管理都有依赖,请使用以下格式:

foo needs udev /dev management and a toolchain w/ featA, featB, featC

18.2.7. Dependencies on features provided by virtual packages

有些功能可以由多个包提供,例如openGL库。

请参阅第18.12节,“虚拟包的基础架构”了解更多关于虚拟包的信息。

18.3. The .mk file


最后,这是最难的部分。创建名为libfoo.mk的文件。它描述了包应该如何下载、配置、构建、安装等。

根据包的类型,.mk文件必须以不同的方式编写,使用不同的基础架构:

进一步的格式化细节:参见 写作规则.

18.4. The .hash file


如果可能的话,你必须添加一个名为libfoo.hash的第三个文件,它包含了libfoo包下载文件的哈希值。不添加.hash文件的唯一原因是由于包的下载方式而无法进行hash检查。

当一个包有版本选择选项时,哈希文件可以存储在以版本命名的子目录中,例如:package/libfoo/1.2.3/libfoo.hash。如果不同版本具有不同的许可条款,但它们存储在同一个文件中,那么这一点尤其重要。否则,散列文件应该留在包的目录中。

存储在该文件中的哈希用于验证下载的文件和许可证文件的完整性。

这个文件的格式是,每个要检查散列值的文件一行,每一行由以下三个字段用两个空格分隔:

#开头的行被认为是注释,会被忽略。空行被忽略。

一个文件可以有多个散列,每个散列在单独的一行上。在这种情况下,所有的散列值必须匹配。

注意. 理想情况下,存储在此文件中的哈希值应该与upstream发布的哈希值匹配,例如,在他们的网站上,在电子邮件公告中……(sha1sha512),然后最好将所有的哈希值添加到.hash文件中。如果upstream没有提供任何哈希值,或者只提供了md5哈希值,那么你自己至少计算一个强哈希值(最好是sha256,而不是md5),并在哈希值上方的注释行中提到这一点。

注意. license文件的哈希用于在包版本发生变化时检测license变化。这些散列值会在make legal-info目标运行时进行检查。对于具有多个版本的包(如Qt5),在该包的子目录<packageversion>中创建哈希文件(参见第19.2节,“如何应用补丁”)。

下面的例子定义了upstream发布的主要libfoo-1.2.3.tar.bz2 tarball的sha1sha256,来自upstream的md5和本地计算的sha256哈希值用于二进制blob, sha256用于下载的补丁,以及一个没有哈希值的归档文件:

# Hashes from: http://www.foosoftware.org/download/libfoo-1.2.3.tar.bz2.{sha1,sha256}:
sha1  486fb55c3efa71148fe07895fd713ea3a5ae343a  libfoo-1.2.3.tar.bz2
sha256  efc8103cc3bcb06bda6a781532d12701eb081ad83e8f90004b39ab81b65d4369  libfoo-1.2.3.tar.bz2

# md5 from: http://www.foosoftware.org/download/libfoo-1.2.3.tar.bz2.md5, sha256 locally computed:
md5  2d608f3c318c6b7557d551a5a09314f03452f1a1  libfoo-data.bin
sha256  01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b  libfoo-data.bin

# Locally computed:
sha256  ff52101fb90bbfc3fe9475e425688c660f46216d7e751c4bbdb1dc85cdccacb9  libfoo-fix-blabla.patch

# Hash for license files:
sha256  a45a845012742796534f7e91fe623262ccfb99460a2bd04015bd28d66fba95b8  COPYING
sha256  01b1f9f2c8ee648a7a596a1abe8aa4ed7899b1c9e5551bda06da6e422b04aa55  doc/COPYING.LGPL

如果.hash文件存在,并且它包含一个或多个下载文件的哈希值,Buildroot(下载后)计算的哈希值必须与存储在.hash文件中的哈希值匹配。如果一个或多个散列不匹配,Buildroot会认为这是一个错误,删除下载的文件并终止。

如果.hash文件存在,但它不包含下载文件的哈希值,Buildroot会认为这是一个错误并终止。然而,下载的文件被留在下载目录中,因为这通常表明.hash文件是错误的,但下载的文件可能是好的。

哈希值目前用于检查从http/ftp服务器获取的文件、Git仓库、使用scp复制的文件和本地文件。没有检查其他版本控制系统(如Subversion、CVS等)的哈希值,因为Buildroot目前在从这些版本控制系统获取源代码时不会生成可重复的压缩包。

哈希值只应该添加到.hash文件中,以保证文件的稳定性。例如,Github自动生成的补丁不能保证是稳定的,因此它们的哈希可能会随着时间的推移而变化。这样的补丁不应该被下载,而是被添加到本地的包文件夹中。

如果.hash文件缺失,则不会进行任何检查。

18.5. The SNNfoo start script


提供系统守护进程的包通常需要在引导时以某种方式启动。Buildroot支持几个init系统,其中一些被认为是第一级(参见第6.3节,“init系统”),而其他也可用,但没有相同的集成级别。理想情况下,所有提供系统守护进程的包都应该提供BusyBox/SysV init的启动脚本和systemd单元文件。

为了一致性,启动脚本必须遵循参考文件中所示的样式和组成:package/busybox/S01syslogd。这个样式的注释示例如下所示。systemd单元文件没有特定的编码风格,但是如果一个包带有它自己的单元文件,如果它与buildroot兼容,则优先于buildroot特定的。

启动脚本的名称由SNN和守护进程的名称组成。NN是起始订单号,需要仔细选择。例如,一个需要联网的程序不应该在S40network之前启动。这些脚本是按照字母顺序启动的,所以S01syslogd会在S01watchdogd之前启动,而S02sysctl则在S01syslogd之后启动。

01: #!/bin/sh
02:
03: DAEMON="syslogd"
04: PIDFILE="/var/run/$DAEMON.pid"
05:
06: SYSLOGD_ARGS=""
07:
08: # shellcheck source=/dev/null
09: [ -r "/etc/default/$DAEMON" ] && . "/etc/default/$DAEMON"
10:
11: # BusyBox' syslogd does not create a pidfile, so pass "-n" in the command line
12: # and use "-m" to instruct start-stop-daemon to create one.
13: start() {
14:     printf 'Starting %s: ' "$DAEMON"
15:     # shellcheck disable=SC2086 # we need the word splitting
16:     start-stop-daemon -b -m -S -q -p "$PIDFILE" -x "/sbin/$DAEMON" \
17:             -- -n $SYSLOGD_ARGS
18:     status=$?
19:     if [ "$status" -eq 0 ]; then
20:             echo "OK"
21:     else
22:             echo "FAIL"
23:     fi
24:     return "$status"
25: }
26:
27: stop() {
28:     printf 'Stopping %s: ' "$DAEMON"
29:     start-stop-daemon -K -q -p "$PIDFILE"
30:     status=$?
31:     if [ "$status" -eq 0 ]; then
32:             rm -f "$PIDFILE"
33:             echo "OK"
34:     else
35:             echo "FAIL"
36:     fi
37:     return "$status"
38: }
39:
40: restart() {
41:     stop
42:     sleep 1
43:     start
44: }
45:
46: case "$1" in
47:     start|stop|restart)
48:             "$1";;
49:     reload)
50:             # Restart, since there is no true "reload" feature.
51:             restart;;
52:     *)
53:             echo "Usage: $0 {start|stop|restart|reload}"
54:             exit 1
55: esac

注意: 支持以某种方式重新加载配置的程序(SIGHUP)应该提供一个类似于stop()reload()函数。start-stop-daemon支持-K -s HUP。建议在所有的start-stop-daemon命令后都加上-x "/sbin/$DAEMON",以确保信号被设置为与$DAEMON匹配的PID。

启动脚本和单元文件都可以从/etc/default/foo中获取命令行参数,一般来说,如果这样的文件不存在,它不应该阻止守护进程的启动,除非有一些站点特定的命令行参数,守护进程需要启动。对于启动脚本,FOO_ARGS="-s -o -m -e -args"可以被定义为默认值,用户可以在/etc/default/foo中覆盖它。

18.6. Infrastructure for packages with specific build systems


所谓特定构建系统的包,是指构建系统不是标准构建系统的所有包,如autotools或CMake。这通常包括构建系统基于手写makefile或shell脚本的包。

18.6.1. generic-package tutorial

01: ################################################################################
02: #
03: # libfoo
04: #
05: ################################################################################
06:
07: LIBFOO_VERSION = 1.0
08: LIBFOO_SOURCE = libfoo-$(LIBFOO_VERSION).tar.gz
09: LIBFOO_SITE = http://www.foosoftware.org/download
10: LIBFOO_LICENSE = GPL-3.0+
11: LIBFOO_LICENSE_FILES = COPYING
12: LIBFOO_INSTALL_STAGING = YES
13: LIBFOO_CONFIG_SCRIPTS = libfoo-config
14: LIBFOO_DEPENDENCIES = host-libaaa libbbb
15:
16: define LIBFOO_BUILD_CMDS
17:     $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) all
18: endef
19:
20: define LIBFOO_INSTALL_STAGING_CMDS
21:     $(INSTALL) -D -m 0755 $(@D)/libfoo.a $(STAGING_DIR)/usr/lib/libfoo.a
22:     $(INSTALL) -D -m 0644 $(@D)/foo.h $(STAGING_DIR)/usr/include/foo.h
23:     $(INSTALL) -D -m 0755 $(@D)/libfoo.so* $(STAGING_DIR)/usr/lib
24: endef
25:
26: define LIBFOO_INSTALL_TARGET_CMDS
27:     $(INSTALL) -D -m 0755 $(@D)/libfoo.so* $(TARGET_DIR)/usr/lib
28:     $(INSTALL) -d -m 0755 $(TARGET_DIR)/etc/foo.d
29: endef
30:
31: define LIBFOO_USERS
32:     foo -1 libfoo -1 * - - - LibFoo daemon
33: endef
34:
35: define LIBFOO_DEVICES
36:     /dev/foo c 666 0 0 42 0 - - -
37: endef
38:
39: define LIBFOO_PERMISSIONS
40:     /bin/foo f 4755 foo libfoo - - - - -
41: endef
42:
43: $(eval $(generic-package))

Makefile从第7行到第11行开始包含元数据信息:包的版本(LIBFOO_VERSION)、包含该包的tar包的名称(LIBFOO_SOURCE) (xz-ed tarball推荐)、可以从互联网上下载tar包的位置(LIBFOO_SITE)、许可证(LIBFOO_LICENSE)和带有许可证文本的文件(LIBFOO_LICENSE_FILES)。所有变量必须以相同的前缀开头,本例中为LIBFOO_。这个前缀始终是包名的大写版本(请参阅下面以了解包名在何处定义)。

在第12行,我们指定这个包要在过渡空间中安装一些东西。对于库来说,这是经常需要的,因为它们必须在过渡空间中安装头文件和其他开发文件。这将确保执行LIBFOO_INSTALL_STAGING_CMDS变量中列出的命令。

在第13行,我们指定要对在LIBFOO_INSTALL_STAGING_CMDS阶段安装的一些libfoo-config文件进行一些修复。这些*-config文件是位于$(STAGING_DIR)/usr/bin目录下的可执行shell脚本文件,由其他第三方包执行,以查找此特定包的位置和链接标志。

问题是所有这些*-config文件默认都给出了错误的主机系统链接标志,不适合交叉编译。

例如:将-I/usr/include替换为-I$(STAGING_DIR)/usr/include或将-L$(STAGING_DIR)/usr/lib替换为-L$(STAGING_DIR)/usr/lib

因此,对这些脚本执行了一些sed魔法,使它们给出正确的标志。传给LIBFOO_CONFIG_SCRIPTS的参数是需要修复的shell脚本的文件名。所有这些名称都是相对于$(STAGING_DIR)/usr/bin的,如果需要,可以提供多个名称。

此外,列出在LIBFOO_CONFIG_SCRIPTS中的脚本将从$(TARGET_DIR)/usr/bin中删除,因为它们在目标上不需要。

Example 18.1. Config script: divine package

安装shell脚本$(STAGING_DIR)/usr/bin/divine-config。

所以它的修正如下:

DIVINE_CONFIG_SCRIPTS = divine-config

Example 18.2. Config script: imagemagick package:

imagemagick包安装以下脚本: $(STAGING_DIR)/usr/bin/{Magick,Magick++,MagickCore,MagickWand,Wand}-config

所以它的修正将是:

IMAGEMAGICK_CONFIG_SCRIPTS = \
   Magick-config Magick++-config \
   MagickCore-config MagickWand-config Wand-config

在第14行,我们指定了这个包依赖的依赖项列表。这些依赖项以小写包名的形式列出,可以是目标包(没有host-前缀)或主机包(有host-前缀)。Buildroot将确保在当前包开始配置之前构建并安装所有这些包。

Makefile的其余部分,第16行..29、定义了在包配置、编译和安装的不同步骤应该做什么。LIBFOO_BUILD_CMDS告诉我们应该执行哪些步骤来构建包。LIBFOO_INSTALL_STAGING_CMDS告诉我们应该执行哪些步骤将包安装到过渡空间中。LIBFOO_INSTALL_TARGET_CMDS告诉我们应该执行哪些步骤将包安装到目标空间中。

所有这些步骤都依赖于$(@D)变量,它包含了包的源代码被提取的目录。

第31..33行、我们定义一个此包使用的用户(例如,以非根用户身份运行守护进程)(LIBFOO_USERS)。

第35…37行、我们定义了这个包使用的设备节点文件(LIBFOO_DEVICES)。

第39..41行、我们定义权限来设置这个包(LIBFOO_PERMISSIONS)安装的特定文件。

最后,在第43行,我们调用generic-package函数,它根据前面定义的变量生成所有使包工作所需的Makefile代码。

18.6.2. generic-package reference

通用目标有两种变体。generic-package宏用于为目标交叉编译包。host-generic-package宏用于主机包,为主机原生编译。可以在一个.mk文件中调用它们:一次创建规则以生成目标包,一次创建规则以生成主机包:

$(eval $(generic-package))
$(eval $(host-generic-package))

如果目标包的编译需要在主机上安装一些工具,这可能很有用。如果包名是libfoo,那么目标的包名也是libfoo,而主机的包名是host-libfoo。如果其他包依赖于libfoohost-libfoo,则应该在它们的依赖变量中使用这些名称。

generic-package和/或host-generic-package宏的调用必须.mk文件的末尾,在所有变量定义之后。调用host-generic-package 必须在调用generic-package之后(如果有的话)。

对于目标包,generic-package使用.mk文件定义的变量,并以大写的包名LIBFOO_*作为前缀。host-generic-package使用HOST_LIBFOO_*变量。对于某些变量,如果前缀为HOST_LIBFOO_的变量不存在,则包基础架构会使用前缀为LIBFOO_的对应变量。如果变量在target包和host包中具有相同的值,则执行此操作。详情见下文。

可以在.mk文件中设置以提供元数据信息的变量列表如下(假设包名为libfoo):

# 0001-fix-cve-2020-12345.patch
LIBFOO_IGNORE_CVES += CVE-2020-12345
# only when built with libbaz, which Buildroot doesn't support
LIBFOO_IGNORE_CVES += CVE-2020-54321

推荐使用下面的语法来定义这些变量:

LIBFOO_VERSION = 2.32

现在是定义在构建过程的不同步骤中应该执行什么操作的变量。

定义这些变量的首选方式是:

define LIBFOO_CONFIGURE_CMDS
        action 1
        action 2
        action 3
endef

在操作定义中,你可以使用以下变量:

最后,你也可以使用hooks。参见 18.23节“不同构建步骤中可用的钩子” 获取更多信息。

18.7. Infrastructure for autotools-based packages


18.7.1. autotools-package tutorial

首先,让我们看看如何为基于autotools的包写一个.mk文件,举个例子:

01: ################################################################################
02: #
03: # libfoo
04: #
05: ################################################################################
06:
07: LIBFOO_VERSION = 1.0
08: LIBFOO_SOURCE = libfoo-$(LIBFOO_VERSION).tar.gz
09: LIBFOO_SITE = http://www.foosoftware.org/download
10: LIBFOO_INSTALL_STAGING = YES
11: LIBFOO_INSTALL_TARGET = NO
12: LIBFOO_CONF_OPTS = --disable-shared
13: LIBFOO_DEPENDENCIES = libglib2 host-pkgconf
14:
15: $(eval $(autotools-package))

第7行声明了包的版本号。

在第8行和第9行,我们声明了压缩包的名称(建议使用xz-ed压缩包)以及压缩包在Web上的位置。Buildroot会自动从这个位置下载压缩包。

第10行告诉Buildroot把这个包安装到过渡目录中。staging目录位于output/staging/中,是所有包的安装目录,包括它们的开发文件等。默认情况下,包不会安装到过渡目录,因为通常只需要将库安装到过渡目录中:它们的开发文件需要用于编译依赖它们的其他库或应用程序。另外,默认情况下,当启用临时安装时,包会使用make install命令安装在此位置。

在第11行,我们告诉Buildroot不要把这个包安装到目标目录。该目录将成为在目标上运行的根文件系统。对于纯粹的静态库,没有必要将它们安装在目标目录中,因为它们不会在运行时使用。默认情况下,目标安装是启用的;几乎不需要将这个变量设置为NO。默认情况下,包也使用make install命令安装在此位置。

在第12行,我们告诉Buildroot传递一个自定义配置选项,它将被传递给./configure脚本,然后配置和构建包。

在第13行,我们声明了依赖,以便在包的构建过程开始之前构建它们。

最后,在第15行,我们调用autotools-package宏,该宏生成所有实际允许构建包的Makefile规则。

18.7.2. autotools-package reference

autotools包基础架构的主要宏是autotools-package。它类似于generic-package宏。也可以使用host-autotools-package宏来获得目标包和主机包。

就像通用基础结构一样,autotools基础结构通过在调用autotools-package宏之前定义许多变量来工作。

首先,在通用基础架构中存在的所有包元数据信息变量也存在于autotools基础架构中: LIBFOO_VERSION, LIBFOO_SOURCE, LIBFOO_PATCH, LIBFOO_SITE, LIBFOO_SUBDIR, LIBFOO_DEPENDENCIES, LIBFOO_INSTALL_STAGING, LIBFOO_INSTALL_TARGET.

还可以定义一些特定于autotools基础架构的额外变量。它们中的许多只在非常特定的情况下有用,因此典型的包只会使用其中的一些。

使用autotools基础架构,已经定义了构建和安装包所需的所有步骤,并且它们通常适用于大多数基于autotools的包。但是,在需要时,仍然可以自定义任何特定步骤的操作:

18.8. Infrastructure for CMake-based packages


18.8.1. cmake-package tutorial

首先,让我们看看如何为基于cmake的包编写.mk文件,下面是一个例子:

01: ################################################################################
02: #
03: # libfoo
04: #
05: ################################################################################
06:
07: LIBFOO_VERSION = 1.0
08: LIBFOO_SOURCE = libfoo-$(LIBFOO_VERSION).tar.gz
09: LIBFOO_SITE = http://www.foosoftware.org/download
10: LIBFOO_INSTALL_STAGING = YES
11: LIBFOO_INSTALL_TARGET = NO
12: LIBFOO_CONF_OPTS = -DBUILD_DEMOS=ON
13: LIBFOO_DEPENDENCIES = libglib2 host-pkgconf
14:
15: $(eval $(cmake-package))

第7行声明了包的版本号。

在第8行和第9行,我们声明了压缩包的名称(建议使用xz-ed压缩包)以及压缩包在Web上的位置。Buildroot会自动从这个位置下载压缩包。

第10行告诉Buildroot把这个包安装到过渡目录中。staging目录位于output/staging/中,是所有包的安装目录,包括它们的开发文件等。默认情况下,包不会安装到过渡目录,因为通常只需要将库安装到过渡目录中:它们的开发文件需要用于编译依赖它们的其他库或应用程序。另外,默认情况下,当启用临时安装时,包会使用make install命令安装在此位置。

在第11行,我们告诉Buildroot不要把这个包安装到目标目录。该目录将成为在目标上运行的根文件系统。对于纯粹的静态库,没有必要将它们安装在目标目录中,因为它们不会在运行时使用。默认情况下,目标安装是启用的;几乎不需要将这个变量设置为NO。默认情况下,包也使用make install命令安装在此位置。

在第12行,我们告诉Buildroot在配置包时向CMake传递自定义选项。

在第13行,我们声明了依赖,以便在包的构建过程开始之前构建它们。

最后,在第15行,我们调用cmake-package宏,该宏生成所有实际允许构建包的Makefile规则。

18.8.2. cmake-package reference

CMake包基础架构的主要宏是cmake-package。它类似于generic-package宏。也可以使用host-cmake-package宏来获取目标包和主机包。

就像通用基础架构一样,CMake基础架构通过在调用cmake-package宏之前定义许多变量来工作。

首先,存在于通用基础架构中的所有包元数据信息变量也存在于CMake基础架构中: LIBFOO_VERSION, LIBFOO_SOURCE, LIBFOO_PATCH, LIBFOO_SITE, LIBFOO_SUBDIR, LIBFOO_DEPENDENCIES, LIBFOO_INSTALL_STAGING, LIBFOO_INSTALL_TARGET.

还可以定义一些特定于CMake基础架构的额外变量。它们中的许多只在非常特定的情况下有用,因此典型的包只会使用其中的一些。

有了CMake基础架构,构建和安装包所需的所有步骤都已经定义好了,它们通常适用于大多数基于CMake的包。但是,在需要时,仍然可以自定义任何特定步骤的操作:

18.9. Infrastructure for Python packages


此基础架构适用于使用标准Python setuptools或pep517机制作为构建系统的Python包,通常可以通过使用setup.py脚本或pyproject.toml文件来识别。

18.9.1. python-package tutorial

首先,让我们看看如何为Python包编写.mk文件,举个例子:

01: ################################################################################
02: #
03: # python-foo
04: #
05: ################################################################################
06:
07: PYTHON_FOO_VERSION = 1.0
08: PYTHON_FOO_SOURCE = python-foo-$(PYTHON_FOO_VERSION).tar.xz
09: PYTHON_FOO_SITE = http://www.foosoftware.org/download
10: PYTHON_FOO_LICENSE = BSD-3-Clause
11: PYTHON_FOO_LICENSE_FILES = LICENSE
12: PYTHON_FOO_ENV = SOME_VAR=1
13: PYTHON_FOO_DEPENDENCIES = libmad
14: PYTHON_FOO_SETUP_TYPE = distutils
15:
16: $(eval $(python-package))

第7行声明了包的版本号。

在第8行和第9行,我们声明了压缩包的名称(建议使用xz-ed压缩包)以及压缩包在Web上的位置。Buildroot会自动从这个位置下载压缩包。

第10行和第11行给出了这个包的许可细节(第10行给出了它的许可,第11行给出了包含许可文本的文件)。

在第12行,我们告诉Buildroot在配置包时将自定义选项传递给Python的setup.py脚本。

在第13行,我们声明了依赖,以便在包的构建过程开始之前构建它们。

第14行声明了要使用的Python构建系统。在这种情况下,使用distutils Python构建系统。支持的四个工具是distutils, flit, pep517setuptools

最后,在第16行,我们调用python-package宏来生成所有实际允许构建包的Makefile规则。

18.9.2. python-package reference

作为一个策略,只提供Python模块的包在Buildroot中都应该命名为python-<something>。其他使用Python构建系统但不是Python模块的包可以自由选择它们的名称(Buildroot中现有的示例是sconssupervisor)。

Python包基础架构的主要宏是python-package。它类似于generic-package宏。也可以使用host-python-package宏创建Python主机包。

就像通用基础架构一样,Python基础架构的工作原理是在调用python-packagehost-python-package宏之前定义许多变量。

通用包基础架构中存在的所有包元数据信息变量也存在于Python基础架构中: PYTHON_FOO_VERSION, PYTHON_FOO_SOURCE, PYTHON_FOO_PATCH, PYTHON_FOO_SITE, PYTHON_FOO_SUBDIR, PYTHON_FOO_DEPENDENCIES, PYTHON_FOO_LICENSE, PYTHON_FOO_LICENSE_FILES, PYTHON_FOO_INSTALL_STAGING, 等.

注意:

有一个特定于Python基础架构的变量是必须的:

根据包的需求,可以选择性地定义一些特定于Python基础架构的额外变量。它们中的许多只在非常特定的情况下有用,因此典型的包将只使用其中的一些,或完全不使用。

有了Python基础架构,构建和安装包所需的所有步骤都已经定义好了,它们通常适用于大多数基于Python的包。但是,在需要时,仍然可以自定义任何特定步骤的操作:

18.9.3. Generating a python-package from a PyPI repository

如果你想创建一个Buildroot包的Python包在PyPI上可用,你可能想使用位于utils/中的scanpypi工具来自动化这个过程。

您可以在这里找到现有PyPI包的列表。

scanpypi需要在主机上安装Python的setuptools包。

当在你的buildroot目录的根目录下执行以下命令:

utils/scanpypi foo bar -o package

这将在包文件夹中生成包python-foopython-bar,如果它们存在于https://pypi.python.org上。

找到external python modules菜单并将你的包插入其中。记住,菜单中的项目应该按照字母顺序排列。

请记住,您很可能必须手动检查软件包是否有任何错误,因为有些东西是生成器猜不到的(例如,依赖于任何python核心模块,如BR2_PACKAGE_PYTHON_ZLIB)。另外,请注意,许可证和许可证文件是猜测的,必须进行检查。你还需要手动将包添加到package/Config.in文件中。

如果你的Buildroot包不在官方的Buildroot树中,而是在br2-external树中,请像下面这样使用-o参数:

utils/scanpypi foo bar -o other_package_dir

这将在other_package_directory中生成python-foopython-bar包,而不是package包。

选项-h将列出可用的选项:

utils/scanpypi -h

18.9.4. python-package CFFI backend

CFFI (C Foreign Function Interface for Python)提供了一种方便可靠的方法,可以使用用C语言编写的接口声明从Python中调用编译后的C代码。Python包依赖于这个后端,可以通过其setup.py文件的install_requires字段中的cffi依赖项来识别。

这样一套方案应:

config BR2_PACKAGE_PYTHON_FOO
        bool "python-foo"
        select BR2_PACKAGE_PYTHON_CFFI # runtime
################################################################################
#
# python-foo
#
################################################################################

...

PYTHON_FOO_DEPENDENCIES = host-python-cffi

$(eval $(python-package))

18.10. Infrastructure for LuaRocks-based packages


18.10.1. luarocks-package tutorial

首先,让我们看看如何为基于LuaRocks的包编写.mk文件,以下是一个例子:

01: ################################################################################
02: #
03: # lua-foo
04: #
05: ################################################################################
06:
07: LUA_FOO_VERSION = 1.0.2-1
08: LUA_FOO_NAME_UPSTREAM = foo
09: LUA_FOO_DEPENDENCIES = bar
10:
11: LUA_FOO_BUILD_OPTS += BAR_INCDIR=$(STAGING_DIR)/usr/include
12: LUA_FOO_BUILD_OPTS += BAR_LIBDIR=$(STAGING_DIR)/usr/lib
13: LUA_FOO_LICENSE = luaFoo license
14: LUA_FOO_LICENSE_FILES = $(LUA_FOO_SUBDIR)/COPYING
15:
16: $(eval $(luarocks-package))

在第7行,我们声明了包的版本(与rockspec中相同,由上游版本和rockspec修订版本连接而成,用连字符 - 分隔)。

在第8行,我们在luarock中声明包名为”foo”。在Buildroot中,我们给lua相关的包起一个以“lua”开头的名字,因此Buildroot的名称与上游名称不同。LUA_FOO_NAME_UPSTREAM建立了两个名称之间的联系。

在第9行,我们声明了本机库的依赖项,以便在我们的包的构建过程开始之前构建它们。

在第11-12行,我们告诉Buildroot在构建包时将自定义选项传递给LuaRocks。

在第13-14行,我们指定了包的许可条款。

最后,在第16行,我们调用luarocks-package宏,该宏生成所有实际允许构建包的Makefile规则。

大多数细节都可以从rockrockspec中获取。这个文件和Config。可以通过在buildroot目录下运行命令luarocks buildroot foo lua-foo来生成。这个命令会运行一个特定的luarocks的Buildroot插件,它会自动生成一个Buildroot包。结果仍然必须手动检查,并可能进行修改。

18.10.2. luarocks-package reference

LuaRocks是一个Lua模块的部署和管理系统,支持各种build.type: builtin, makecmake。在Buildroot的上下文中,luarocks-package基础架构只支持builtin模式。使用makecmake构建机制的luarock包应该分别使用Buildroot中的generic-packagecmake-package基础架构进行打包。

LuaRocks包基础架构的主要宏是luarocks-package:像generic-package一样,它通过定义一些提供包元数据信息的变量来工作,然后调用luarocks-package

就像通用基础架构一样,LuaRocks基础架构通过在调用luarocks-package宏之前定义许多变量来工作。

首先,存在于通用基础架构中的所有包元数据信息变量也存在于LuaRocks基础架构中: LUA_FOO_VERSION, LUA_FOO_SOURCE, LUA_FOO_SITE, LUA_FOO_DEPENDENCIES, LUA_FOO_LICENSE, LUA_FOO_LICENSE_FILES.

其中两个由LuaRocks基础架构填充(用于“下载”步骤)。如果你的包没有托管在LuaRocks镜像$(BR2_LUAROCKS_MIRROR)上,你可以覆盖它们:

A few additional variables, specific to the LuaRocks infrastructure, are also defined. They can be overridden in specific cases.

18.11. Infrastructure for Perl/CPAN packages


18.11.1. perl-package tutorial

首先,让我们看看如何为Perl/CPAN包编写.mk文件,举个例子:

01: ################################################################################
02: #
03: # perl-foo-bar
04: #
05: ################################################################################
06:
07: PERL_FOO_BAR_VERSION = 0.02
08: PERL_FOO_BAR_SOURCE = Foo-Bar-$(PERL_FOO_BAR_VERSION).tar.gz
09: PERL_FOO_BAR_SITE = $(BR2_CPAN_MIRROR)/authors/id/M/MO/MONGER
10: PERL_FOO_BAR_DEPENDENCIES = perl-strictures
11: PERL_FOO_BAR_LICENSE = Artistic or GPL-1.0+
12: PERL_FOO_BAR_LICENSE_FILES = LICENSE
13: PERL_FOO_BAR_DISTNAME = Foo-Bar
14:
15: $(eval $(perl-package))

第7行声明了包的版本号。

在第8行和第9行,我们声明了压缩包的名称以及压缩包在CPAN服务器上的位置。Buildroot会自动从这个位置下载压缩包。

在第10行,我们声明了依赖项,以便在包的构建过程开始之前构建它们。

第11行和第12行给出了这个包的许可细节(第11行给出了它的许可,第12行给出了包含许可文本的文件)。

第13行是脚本utils/scancpan所需的发行版名称(以便重新生成/升级这些包文件)。

最后,在第15行,我们调用perl-package宏,该宏生成所有实际允许构建包的Makefile规则。

大多数这些数据可以从https://metacpan.org/检索。因此,这个文件和配置文件可以通过在Buildroot目录(或br2-external树)中运行utils/scancpan Foo-Bar脚本来生成。此脚本为所请求的包创建Config.in文件和foo-bar.mk文件,并为CPAN指定的所有依赖项创建递归。你仍然应该手动编辑结果。特别应检查以下事项。

18.11.2. perl-package reference

作为一个策略,提供Perl/CPAN模块的包在Buildroot中都应该被命名为perl-<something>

这个基础架构处理各种Perl构建系统:ExtUtils-MakeMaker(EUMM)、Module-Build (MB)和Module-Build-Tiny。当包提供了Makefile.PLBuild.PL时,默认优先使用Build.PL

Perl/CPAN包基础架构的主要宏是perl-package。它类似于generic-package宏。也可以使用host-perl-package宏来获得目标包和主机包。

就像一般的基础结构一样,Perl/CPAN基础结构通过在调用perl-package宏之前定义许多变量来工作。

首先,存在于通用基础架构中的所有包元数据信息变量也存在于Perl/CPAN基础架构中: PERL_FOO_VERSION, PERL_FOO_SOURCE, PERL_FOO_PATCH, PERL_FOO_SITE, PERL_FOO_SUBDIR, PERL_FOO_DEPENDENCIES, PERL_FOO_INSTALL_TARGET.

请注意,除非定义了PERL_FOO_INSTALL_STAGING_CMDS变量,否则将PERL_FOO_INSTALL_STAGING设置为YES是无效的。perl基础架构没有定义这些命令,因为perl模块通常不需要安装到staging目录。

还可以定义一些特定于Perl/CPAN基础结构的额外变量。它们中的许多只在非常特定的情况下有用,因此典型的包只会使用其中的一些。

18.12. Infrastructure for virtual packages


在Buildroot中,虚拟包是由一个或多个包(称为提供者)提供功能的包。虚拟包管理是一种可扩展的机制,允许用户选择在rootfs中使用的提供者。

例如,OpenGL ES是嵌入式系统上用于2D和3D图形的API。对于Allwinner Tech Sunxi和Texas Instruments的OMAP35xx平台,此API的实现是不同的。所以libgles是一个虚拟包,sunxi-mali-utgardti-gfx是它的提供者。

18.12.1. virtual-package tutorial

在下面的例子中,我们将解释如何添加一个新的虚拟包(something-virtual)和它的提供者(some-provider)。

首先,让我们创建虚拟包。

18.12.2. Virtual package’s Config.in file

虚拟包something-virtual的Config.in文件应该包含:

01: config BR2_PACKAGE_HAS_SOMETHING_VIRTUAL
02:     bool
03:
04: config BR2_PACKAGE_PROVIDES_SOMETHING_VIRTUAL
05:     depends on BR2_PACKAGE_HAS_SOMETHING_VIRTUAL
06:     string

在这个文件中,我们声明了两个选项,BR2_PACKAGE_HAS_SOMETHING_VIRTUALBR2_PACKAGE_PROVIDES_SOMETHING_VIRTUAL,它们的值将被providers使用。

18.12.3. Virtual package’s .mk file

虚拟包的.mk应该只计算virtual-package宏:

01: ################################################################################
02: #
03: # something-virtual
04: #
05: ################################################################################
06:
07: $(eval $(virtual-package))

也可以使用host-virtual-package宏来获得目标包和主机包。

18.12.4. Provider’s Config.in file

当添加一个包作为provider时,只有Config.in文件需要一些修改。

some-provider包的Config.in文件提供了something-virtual的功能,它应该包含:

01: config BR2_PACKAGE_SOME_PROVIDER
02:     bool "some-provider"
03:     select BR2_PACKAGE_HAS_SOMETHING_VIRTUAL
04:     help
05:       This is a comment that explains what some-provider is.
06:
07:       http://foosoftware.org/some-provider/
08:
09: if BR2_PACKAGE_SOME_PROVIDER
10: config BR2_PACKAGE_PROVIDES_SOMETHING_VIRTUAL
11:     default "some-provider"
12: endif

在第3行,我们选择了BR2_PACKAGE_HAS_SOMETHING_VIRTUAL,在第11行,我们将BR2_PACKAGE_PROVIDES_SOMETHING_VIRTUAL的值设置为提供者的名称,但仅当它被选中时才设置。

18.12.5. Provider’s .mk file

.mk文件还应该声明一个额外的变量SOME_PROVIDER_PROVIDES来包含它所实现的所有虚拟包的名称:

01: SOME_PROVIDER_PROVIDES = something-virtual

当然,不要忘记为这个包添加适当的构建和运行时依赖项!

18.12.6. Notes on depending on a virtual package

当添加一个需要由虚拟包提供的特定功能的包时,你必须使用depends on BR2_PACKAGE_HAS_FEATURE,如下所示:

config BR2_PACKAGE_HAS_FEATURE
    bool

config BR2_PACKAGE_FOO
    bool "foo"
    depends on BR2_PACKAGE_HAS_FEATURE

18.12.7. Notes on depending on a specific provider

如果你的包确实需要一个特定的provider,那么你必须让你的包depends on这个provider;你不能select一个provider。

让我们以一个有两个providers的FEATURE为例:

config BR2_PACKAGE_HAS_FEATURE
    bool

config BR2_PACKAGE_FOO
    bool "foo"
    select BR2_PACKAGE_HAS_FEATURE

config BR2_PACKAGE_BAR
    bool "bar"
    select BR2_PACKAGE_HAS_FEATURE

你正在添加一个包,它需要foo提供的FEATURE,但不需要bar提供的FEATURE

如果你使用了select BR2_PACKAGE_FOO,那么用户仍然可以在菜单配置中选择BR2_PACKAGE_BAR。这将导致配置不一致,即两个具有相同FEATURE的提供者将同时启用,一个由用户显式设置,另一个由您的select隐式设置。

相反,你必须使用depends on BR2_PACKAGE_FOO,这可以避免任何隐式配置的不一致。

18.13. Infrastructure for packages using kconfig for configuration files


软件包处理用户指定配置的一种流行方式是kconfig。其中,它由Linux内核、Busybox和Buildroot本身使用。出现.config文件和menuconfig目标是使用kconfig时的两个众所周知的症状。

Buildroot为使用kconfig进行配置的包提供了一个基础架构。这个基础架构提供了必要的逻辑,以便在Buildroot中将包的menuconfig目标暴露为foo-menuconfig,并以正确的方式处理配置文件的复制。

kconfig-package基础架构基于generic-package基础架构。generic-package支持的所有变量也可以在kconfig-package中使用。请参阅第18.6.2节,“generic-package引用”了解更多细节。

为了在Buildroot包中使用kconfig-package基础架构,除了generic-package基础架构所需的变量之外,.mk文件中最少需要的行包括:

FOO_KCONFIG_FILE = reference-to-source-configuration-file

$(eval $(kconfig-package))

这段代码创建了以下make目标:

并确保在正确的时刻将源配置文件复制到构建目录。

有两个选项可以指定要使用的配置文件,FOO_KCONFIG_FILE(如上例所示)或FOO_KCONFIG_DEFCONFIG。必须提供其中一个,但不能同时提供两个:

除了这些最低要求的行之外,还可以设置几个可选变量,以适应所考虑的包的需求:

18.14. Infrastructure for rebar-based packages


18.14.1. rebar-package tutorial

首先,让我们看看如何为基于rebar的包编写.mk文件,并提供一个示例:

01: ################################################################################
02: #
03: # erlang-foobar
04: #
05: ################################################################################
06:
07: ERLANG_FOOBAR_VERSION = 1.0
08: ERLANG_FOOBAR_SOURCE = erlang-foobar-$(ERLANG_FOOBAR_VERSION).tar.xz
09: ERLANG_FOOBAR_SITE = http://www.foosoftware.org/download
10: ERLANG_FOOBAR_DEPENDENCIES = host-libaaa libbbb
11:
12: $(eval $(rebar-package))

第7行声明了包的版本号。

在第8行和第9行,我们声明了压缩包的名称(建议使用xz-ed压缩包)以及压缩包在Web上的位置。Buildroot会自动从这个位置下载压缩包。

在第10行,我们声明了依赖项,以便在包的构建过程开始之前构建它们。

最后,在第12行,我们调用rebar-package宏,它生成所有实际允许构建包的Makefile规则。

18.14.2. rebar-package reference

rebar包基础架构的主要宏是rebar-package。它类似于generic-package宏。也可以使用host-rebar-package宏来获得主机包。

就像通用基础结构一样,rebar基础结构通过在调用rebar-package宏之前定义许多变量来工作。

首先,存在于通用基础架构中的所有包元数据信息变量也存在于rebar基础架构中: ERLANG_FOOBAR_VERSION, ERLANG_FOOBAR_SOURCE, ERLANG_FOOBAR_PATCH, ERLANG_FOOBAR_SITE, ERLANG_FOOBAR_SUBDIR, ERLANG_FOOBAR_DEPENDENCIES, ERLANG_FOOBAR_INSTALL_STAGING, ERLANG_FOOBAR_INSTALL_TARGET, ERLANG_FOOBAR_LICENSEERLANG_FOOBAR_LICENSE_FILES.

还可以定义一些特定于rebar基础架构的额外变量。它们中的许多只在非常特定的情况下有用,因此典型的包只会使用其中的一些。

使用rebar基础架构,已经定义了构建和安装包所需的所有步骤,它们通常适用于大多数基于rebar的包。但是,在需要时,仍然可以自定义任何特定步骤的操作:

18.15. Infrastructure for Waf-based packages


18.15.1. waf-package tutorial

首先,让我们看看如何为基于Waf的包编写.mk文件,并给出一个示例:

01: ################################################################################
02: #
03: # libfoo
04: #
05: ################################################################################
06:
07: LIBFOO_VERSION = 1.0
08: LIBFOO_SOURCE = libfoo-$(LIBFOO_VERSION).tar.gz
09: LIBFOO_SITE = http://www.foosoftware.org/download
10: LIBFOO_CONF_OPTS = --enable-bar --disable-baz
11: LIBFOO_DEPENDENCIES = bar
12:
13: $(eval $(waf-package))

第7行声明了包的版本号。

在第8行和第9行,我们声明了压缩包的名称(建议使用xz-ed压缩包)以及压缩包在Web上的位置。Buildroot会自动从这个位置下载压缩包。

第10行告诉Buildroot要为libfoo启用哪些选项。

在第11行,我们告诉Buildroot libfoo的依赖。

最后,在第13行,我们调用waf-package宏,该宏生成所有实际允许构建包的Makefile规则。

18.15.2. waf-package reference

Waf包基础架构的主要宏是waf-package。它类似于generic-package宏。

就像通用基础架构一样,Waf基础架构通过在调用waf-package宏之前定义许多变量来工作。

首先,存在于通用基础结构中的所有包元数据信息变量也存在于Waf基础结构中: LIBFOO_VERSION, LIBFOO_SOURCE, LIBFOO_PATCH, LIBFOO_SITE, LIBFOO_SUBDIR, LIBFOO_DEPENDENCIES, LIBFOO_INSTALL_STAGING, LIBFOO_INSTALL_TARGET.

还可以定义一个特定于Waf基础结构的附加变量。

18.16. Infrastructure for Meson-based packages


18.16.1. meson-package tutorial

Meson是一个开源构建系统,其速度非常快,更重要的是,对用户尽可能友好。它使用Ninja作为辅助工具来执行实际的构建操作。

让我们看看如何为基于介子的包编写.mk文件,举个例子:

01: ################################################################################
02: #
03: # foo
04: #
05: ################################################################################
06:
07: FOO_VERSION = 1.0
08: FOO_SOURCE = foo-$(FOO_VERSION).tar.gz
09: FOO_SITE = http://www.foosoftware.org/download
10: FOO_LICENSE = GPL-3.0+
11: FOO_LICENSE_FILES = COPYING
12: FOO_INSTALL_STAGING = YES
13:
14: FOO_DEPENDENCIES = host-pkgconf bar
15:
16: ifeq ($(BR2_PACKAGE_BAZ),y)
17: FOO_CONF_OPTS += -Dbaz=true
18: FOO_DEPENDENCIES += baz
19: else
20: FOO_CONF_OPTS += -Dbaz=false
21: endif
22:
23: $(eval $(meson-package))

Makefile从包声明的标准变量的定义开始(第7行到第11行)。

在第23行,我们调用meson-package宏,该宏生成所有实际允许构建包的Makefile规则。

在这个例子中,host-pkgconfbar在第14行FOO_DEPENDENCIES中被声明为依赖项,因为foo的Meson构建文件使用pkg-config来确定bar包的编译标志和库。

请注意,没有必要在包的FOO_DEPENDENCIES变量中添加host-meson,因为这个基本依赖项会根据Meson包基础架构的需要自动添加。

如果选中了“baz”包,那么通过在第17行向FOO_CONF_OPTS添加-Dbaz=true来激活“foo”中对“baz”特性的支持,如“foo”源代码树中的meson_options.txt文件中指定的那样。“baz”包也被添加到FOO_DEPENDENCIES中。请注意,如果没有选择包,对baz的支持在第20行显式禁用。

总而言之,要添加一个新的基于介子的包,可以逐字复制Makefile示例,然后编辑它,将所有出现的FOO替换为新包的大写名称,并更新标准变量的值。

18.16.2. meson-package reference

介子包基础结构的主要宏是meson-package。它类似于generic-package宏。通过host-meson-package宏,也可以获得目标包和主机包。

就像通用基础结构一样,Meson基础结构通过在调用meson-package宏之前定义许多变量来工作。

首先,存在于通用基础结构中的所有包元数据信息变量也存在于介子基础结构中: FOO_VERSION, FOO_SOURCE, FOO_PATCH, FOO_SITE, FOO_SUBDIR, FOO_DEPENDENCIES, FOO_INSTALL_STAGING, FOO_INSTALL_TARGET.

还可以定义一些特定于Meson基础结构的额外变量。它们中的许多只在非常特定的情况下有用,因此典型的包只会使用其中的一些。

18.17. Infrastructure for Cargo-based packages


Cargo是Rust编程语言的包管理器。它允许用户构建用Rust编写的程序或库,但它也下载和管理它们的依赖项,以确保可重复构建。货物包裹被称为“crates”。

18.17.1. cargo-package tutorial

基于Cargo的foo包的Config.in文件应该包含:

01: config BR2_PACKAGE_FOO
02:     bool "foo"
03:     depends on BR2_PACKAGE_HOST_RUSTC_TARGET_ARCH_SUPPORTS
04:     select BR2_PACKAGE_HOST_RUSTC
05:     help
06:       This is a comment that explains what foo is.
07:
08:       http://foosoftware.org/foo/

这个包的.mk文件应该包含:

01: ################################################################################
02: #
03: # foo
04: #
05: ################################################################################
06:
07: FOO_VERSION = 1.0
08: FOO_SOURCE = foo-$(FOO_VERSION).tar.gz
09: FOO_SITE = http://www.foosoftware.org/download
10: FOO_LICENSE = GPL-3.0+
11: FOO_LICENSE_FILES = COPYING
12:
13: $(eval $(cargo-package))

Makefile从包声明的标准变量的定义开始(第7行到第11行)。

如第13行所示,它基于cargo-package基础架构。Cargo将由此基础架构自动调用,以构建和安装包。

仍然可以定义自定义的构建命令或安装命令(即FOO_BUILD_CMDS和FOO_INSTALL_TARGET_CMDS)。这些将取代来自货运基础架构的命令。

18.17.2. cargo-package reference

Cargo包基础架构的主要宏是目标包的cargo-package和主包的host-cargo-package

就像通用基础结构一样,Cargo基础结构通过在调用cargo-packagehost-cargo-package宏之前定义一系列变量来工作。

首先,存在于通用基础架构中的所有包元数据信息变量也存在于Cargo基础架构中: FOO_VERSION, FOO_SOURCE, FOO_PATCH, FOO_SITE, FOO_DEPENDENCIES, FOO_LICENSE, FOO_LICENSE_FILES, 等.

还可以定义一些特定于Cargo基础架构的额外变量。它们中的许多只在非常特定的情况下有用,因此典型的包只会使用其中的一些。

一个crate可以依赖于crates中的其他库。在它的Cargo.toml文件中列出。Buildroot会自动下载这些依赖项,作为使用cargo-package基础架构的包下载步骤的一部分。然后,这些依赖项与Buildroot的DL_DIR中缓存的包源代码一起保存在tarball中,因此包的tarball的哈希值就包含了这些依赖项。

这种机制可以确保检测到依赖项中的任何更改,并允许完全脱机执行构建。

18.18. Infrastructure for Go packages


此基础架构适用于使用标准构建系统并使用打包依赖项的Go软件包。

18.18.1. golang-package tutorial

首先,让我们看看如何为go包编写一个.mk文件,举个例子:

01: ################################################################################
02: #
03: # foo
04: #
05: ################################################################################
06:
07: FOO_VERSION = 1.0
08: FOO_SITE = $(call github,bar,foo,$(FOO_VERSION))
09: FOO_LICENSE = BSD-3-Clause
10: FOO_LICENSE_FILES = LICENSE
11:
12: $(eval $(golang-package))

第7行声明了包的版本号。

在第8行,我们声明了包的上游位置,这里是从Github获取的,因为大量的Go包托管在Github上。

第9行和第10行给出了这个包的许可细节。

最后,在第12行,我们调用golang-package宏,该宏生成所有实际允许构建包的Makefile规则。

18.18.2. golang-package reference

在他们的Config.in文件中,使用golang-package基础架构的包应该依赖于BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS,因为Buildroot会自动为这些包添加对host-go的依赖。如果你需要在你的包中支持CGO,你必须添加一个依赖BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS

Go包基础架构的主要宏是golang-package。它类似于generic-package宏。也可以使用host-golang-package宏构建主机包。由host-golang-package宏构建的主机包应该依赖于BR2_PACKAGE_HOST_GO_HOST_ARCH_SUPPORTS。

就像通用基础架构一样,Go基础架构的工作方式是在调用golang-package之前定义许多变量。

通用包基础架构中存在的所有包元数据信息变量也存在于Go基础架构中: FOO_VERSION, FOO_SOURCE, FOO_PATCH, FOO_SITE, FOO_SUBDIR, FOO_DEPENDENCIES, FOO_LICENSE, FOO_LICENSE_FILES, FOO_INSTALL_STAGING, 等。

请注意,没有必要在包的FOO_DEPENDENCIES变量中添加host-go,因为这个基本依赖项会根据Go包基础架构的需要自动添加。

根据包的需求,可以选择性地定义一些特定于Go基础架构的额外变量。它们中的许多只在非常特定的情况下有用,因此典型的包将只使用其中的一些,或完全不使用。

使用Go基础架构,构建和安装包所需的所有步骤都已定义,并且它们通常适用于大多数基于Go的包。但是,在需要时,仍然可以自定义任何特定步骤的操作:

Go包可以依赖于其go.mod文件中列出的其他Go模块。Buildroot会自动下载这些依赖项,作为使用golang-package基础架构的包下载步骤的一部分。然后,这些依赖项与Buildroot的DL_DIR中缓存的包源代码一起保存在tarball中,因此包的tarball的哈希值就包含了这些依赖项。

这种机制可以确保检测到依赖项中的任何更改,并允许完全脱机执行构建。

18.19. Infrastructure for QMake-based packages


18.19.1. qmake-package tutorial

首先,让我们看看如何为基于QMake的包写一个.mk文件,举个例子:

01: ################################################################################
02: #
03: # libfoo
04: #
05: ################################################################################
06:
07: LIBFOO_VERSION = 1.0
08: LIBFOO_SOURCE = libfoo-$(LIBFOO_VERSION).tar.gz
09: LIBFOO_SITE = http://www.foosoftware.org/download
10: LIBFOO_CONF_OPTS = QT_CONFIG+=bar QT_CONFIG-=baz
11: LIBFOO_DEPENDENCIES = bar
12:
13: $(eval $(qmake-package))

第7行声明了包的版本号。

在第8行和第9行,我们声明了压缩包的名称(建议使用xz-ed压缩包)以及压缩包在Web上的位置。Buildroot会自动从这个位置下载压缩包。

第10行告诉Buildroot要为libfoo启用哪些选项。

在第11行,我们告诉Buildroot libfoo的依赖。

最后,在第13行,我们调用qmake-package宏,该宏生成所有实际允许构建包的Makefile规则。

18.19.2. qmake-package reference

QMake包基础架构的主要宏是qmake-package。它类似于generic-package宏。

就像通用基础架构一样,QMake基础架构通过在调用qmake-package宏之前定义许多变量来工作。

首先,存在于通用基础架构中的所有包元数据信息变量也存在于QMake基础架构中: LIBFOO_VERSION, LIBFOO_SOURCE, LIBFOO_PATCH, LIBFOO_SITE, LIBFOO_SUBDIR, LIBFOO_DEPENDENCIES, LIBFOO_INSTALL_STAGING, LIBFOO_INSTALL_TARGET.

还可以定义一个特定于QMake基础架构的额外变量。

18.20. Infrastructure for packages building kernel modules


Buildroot提供了一个辅助基础架构,使编写构建和安装Linux内核模块的包变得容易。有些包只包含一个内核模块,其他包除了内核模块之外还包含程序和库。Buildroot的helper基础架构支持这两种情况。

18.20.1. kernel-module tutorial

让我们从一个示例开始,说明如何准备一个只构建内核模块而不构建其他组件的简单包:

01: ################################################################################
02: #
03: # foo
04: #
05: ################################################################################
06:
07: FOO_VERSION = 1.2.3
08: FOO_SOURCE = foo-$(FOO_VERSION).tar.xz
09: FOO_SITE = http://www.foosoftware.org/download
10: FOO_LICENSE = GPL-2.0
11: FOO_LICENSE_FILES = COPYING
12:
13: $(eval $(kernel-module))
14: $(eval $(generic-package))

第7-11行定义了常用的元数据,用于指定版本、存档名称、在何处查找包源的远程URI、许可信息。

在第13行,我们调用kernel-module辅助基础架构,它生成所有适当的Makefile规则和变量来构建该内核模块。

最后,在第14行,我们调用了generic-package基础架构

linux依赖项会自动添加,因此不需要在FOO_DEPENDENCIES中指定它。

您可能已经注意到,与其他包基础结构不同,我们显式调用第二个基础结构。这使得一个包可以构建一个内核模块,如果需要,还可以使用任何其他包基础结构来构建普通的用户层组件(库、可执行文件……)。仅使用kernel-module基础架构是不够的;必须使用另一个包基础架构。

让我们看一个更复杂的例子:

01: ################################################################################
02: #
03: # foo
04: #
05: ################################################################################
06:
07: FOO_VERSION = 1.2.3
08: FOO_SOURCE = foo-$(FOO_VERSION).tar.xz
09: FOO_SITE = http://www.foosoftware.org/download
10: FOO_LICENSE = GPL-2.0
11: FOO_LICENSE_FILES = COPYING
12:
13: FOO_MODULE_SUBDIRS = driver/base
14: FOO_MODULE_MAKE_OPTS = KVERSION=$(LINUX_VERSION_PROBED)
15:
16: ifeq ($(BR2_PACKAGE_LIBBAR),y)
17: FOO_DEPENDENCIES += libbar
18: FOO_CONF_OPTS += --enable-bar
19: FOO_MODULE_SUBDIRS += driver/bar
20: else
21: FOO_CONF_OPTS += --disable-bar
22: endif
23:
24: $(eval $(kernel-module))
26: $(eval $(autotools-package))

在这里,我们看到我们有一个基于autotools的包,它还构建位于子目录driver/base中的内核模块,如果启用libbar,则构建位于子目录driver/bar中的内核模块,并定义在构建模块时传递给Linux构建系统的变量KVERSION

18.20.2. kernel-module reference

内核模块基础架构的主要宏是kernel-module。与其他包基础结构不同,它不是独立的,并且需要在它之后调用任何其他*-package宏。

kernel-module宏定义了post-build和post-target-install钩子来构建内核模块。如果包的.mk需要访问构建的内核模块,它应该在调用kernel-module之后注册的post-build钩子中这样做。类似地,如果包的.mk需要在安装后访问内核模块,它应该在调用kernel-module注册的安装后钩子中这样做。下面是一个例子:

$(eval $(kernel-module))

define FOO_DO_STUFF_WITH_KERNEL_MODULE
    # Do something with it...
endef
FOO_POST_BUILD_HOOKS += FOO_DO_STUFF_WITH_KERNEL_MODULE

$(eval $(generic-package))

最后,与其他包架构不同的是,没有host-kernel-module变体来构建宿主内核模块。

可选地定义下列额外变量,以进一步配置内核模块的构建:

你也可以引用(但不能设置!)这些变量:

18.21. Infrastructure for asciidoc documents


你正在阅读的Buildroot手册完全是使用AsciiDoc标记语法编写的。手册会被渲染成多种格式:

尽管Buildroot只包含一个用AsciiDoc编写的文档,但就像包一样,有一个使用AsciiDoc语法渲染文档的基础架构。

同样对于包,AsciiDoc的基础架构可以从br2-external树中获取。这允许br2-external树的文档与Buildroot文档匹配,因为它将呈现相同的格式,使用相同的布局和主题。

18.21.1. asciidoc-document tutorial

包基础结构的后缀是-package,而文档基础结构的后缀是-document。因此,AsciiDoc基础架构被命名为asciidoc-document

下面是一个渲染简单AsciiDoc文档的例子。

01: ################################################################################
02: #
03: # foo-document
04: #
05: ################################################################################
06:
07: FOO_SOURCES = $(sort $(wildcard $(FOO_DOCDIR)/*))
08: $(eval $(call asciidoc-document))

在第7行,Makefile声明文档的源是什么。目前,文档的来源只能是本地的;Buildroot不会尝试下载任何东西来渲染文档。因此,您必须指明源的位置。通常,上面的字符串对于没有子目录结构的文档已经足够了。

在第8行,我们调用asciidoc-document函数,它会生成渲染文档所需的所有Makefile代码。

18.21.2. asciidoc-document reference

可以在.mk文件中设置用于提供元数据信息的变量列表如下(假设文档名称为foo):

还有其他钩子(有关钩子的一般信息,请参阅第18.23节,“在各种构建步骤中可用的钩子”),文档可以设置为定义在各个步骤中要执行的额外操作:

Buildroot设置以下变量,可以在上面的定义中使用:

下面是一个使用了所有变量和所有钩子的完整示例:

01: ################################################################################
02: #
03: # foo-document
04: #
05: ################################################################################
06:
07: FOO_SOURCES = $(sort $(wildcard $(FOO_DOCDIR)/*))
08: FOO_RESOURCES = $(sort $(wildcard $(FOO_DOCDIR)/ressources))
09:
10: FOO_TOC_DEPTH = 2
11: FOO_TOC_DEPTH_HTML = 1
12: FOO_TOC_DEPTH_SPLIT_HTML = 3
13:
14: define FOO_GEN_EXTRA_DOC
15:     /path/to/generate-script --outdir=$(@D)
16: endef
17: FOO_POST_RSYNC_HOOKS += FOO_GEN_EXTRA_DOC
18:
19: define FOO_CHECK_MY_PROG
20:     if ! which my-prog >/dev/null 2>&1; then \
21:         echo "You need my-prog to generate the foo document"; \
22:         exit 1; \
23:     fi
24: endef
25: FOO_CHECK_DEPENDENCIES_HOOKS += FOO_CHECK_MY_PROG
26:
27: define FOO_CHECK_MY_OTHER_PROG
28:     if ! which my-other-prog >/dev/null 2>&1; then \
29:         echo "You need my-other-prog to generate the foo document as PDF"; \
30:         exit 1; \
31:     fi
32: endef
33: FOO_CHECK_DEPENDENCIES_PDF_HOOKS += FOO_CHECK_MY_OTHER_PROG
34:
35: $(eval $(call asciidoc-document))

18.22. Infrastructure specific to the Linux kernel package


Linux内核包可以使用一些基于包钩子的特定基础结构来构建Linux内核工具或/和构建Linux内核扩展。

18.22.1. linux-kernel-tools

Buildroot提供了一个辅助基础架构,用于为Linux内核源代码中的目标构建一些用户空间工具。由于它们的源代码是内核源代码的一部分,因此存在一个特殊的包,linux-tools,并重用在目标上运行的Linux内核源代码。

让我们看一个Linux工具的例子。对于名为foo的新Linux工具,在现有的package/linux-tools/Config.in中创建一个新的菜单项。该文件将包含与将使用并显示在配置工具中的每个内核工具相关的选项描述。它基本上看起来像这样:

01: config BR2_PACKAGE_LINUX_TOOLS_FOO
02:     bool "foo"
03:     select BR2_PACKAGE_LINUX_TOOLS
04:     help
05:       This is a comment that explains what foo kernel tool is.
06:
07:       http://foosoftware.org/foo/

选项的名称以前缀BR2_PACKAGE_LINUX_TOOLS_开始,后面是工具的大写名称(就像包一样)。

注意. 与其他包不同,linux-tools包选项出现在linux内核菜单中,在Linux Kernel Tools子菜单下,而不是在Target packages主菜单下。

然后为每个linux工具添加一个新的.mk.in文件,名为package/linux-tools/linux-tool-foo.mk.in。它基本上看起来像这样:

01: ################################################################################
02: #
03: # foo
04: #
05: ################################################################################
06:
07: LINUX_TOOLS += foo
08:
09: FOO_DEPENDENCIES = libbbb
10:
11: define FOO_BUILD_CMDS
12:     $(TARGET_MAKE_ENV) $(MAKE) -C $(LINUX_DIR)/tools foo
13: endef
14:
15: define FOO_INSTALL_STAGING_CMDS
16:     $(TARGET_MAKE_ENV) $(MAKE) -C $(LINUX_DIR)/tools \
17:             DESTDIR=$(STAGING_DIR) \
18:             foo_install
19: endef
20:
21: define FOO_INSTALL_TARGET_CMDS
22:     $(TARGET_MAKE_ENV) $(MAKE) -C $(LINUX_DIR)/tools \
23:             DESTDIR=$(TARGET_DIR) \
24:             foo_install
25: endef

在第7行,我们将Linux工具foo注册到可用的Linux工具列表中。

在第9行,我们指定了该工具依赖的依赖项列表。只有当选择foo工具时,这些依赖项才会添加到Linux包依赖列表中。

Makefile的其余部分,第11-25行定义了在Linux工具构建过程的不同步骤中应该做什么,例如对于通用包。它们实际上只会在foo工具被选中时使用。唯一支持的命令是_BUILD_CMDS, _INSTALL_STAGING_CMDS_INSTALL_TARGET_CMDS

注意. 不能调用$(eval $(generic-package))或任何其他包基础架构! Linux工具本身不是包,它们是linux-tools包的一部分。

18.22.2. linux-kernel-extensions

有些包提供了需要修改Linux内核树的新特性。这可以是以补丁的形式应用到内核树,也可以是以向内核树添加新文件的形式。Buildroot的Linux内核扩展基础架构提供了一个简单的解决方案,可以在提取内核源代码之后、应用内核补丁之前自动完成这项工作。使用这种机制打包的扩展示例包括实时扩展Xenomai和RTAI,以及一组树外 LCD屏幕驱动程序fbtft

让我们看一个如何添加新的Linux扩展foo的例子。

首先,创建提供扩展的包foo:这个包是一个标准包;请参阅前面的章节,了解如何创建这样的包。这个包负责下载源代码存档、检查散列、定义许可证信息并构建用户空间工具(如果有的话)。

然后正确地创建Linux扩展:在现有的linux/Config.ext.in中创建一个新的菜单项。该文件包含了与将在配置工具中使用和显示的每个内核扩展相关的选项描述。它基本上看起来像这样:

01: config BR2_LINUX_KERNEL_EXT_FOO
02:     bool "foo"
03:     help
04:       This is a comment that explains what foo kernel extension is.
05:
06:       http://foosoftware.org/foo/

然后,为每个linux扩展添加一个名为linux/linux-ext-foo.mk的新.mk文件。它基本上应该包含:

01: ################################################################################
02: #
03: # foo
04: #
05: ################################################################################
06:
07: LINUX_EXTENSIONS += foo
08:
09: define FOO_PREPARE_KERNEL
10:     $(FOO_DIR)/prepare-kernel-tree.sh --linux-dir=$(@D)
11: endef

在第7行,我们将Linux扩展foo添加到可用的Linux扩展列表中。

在第9-11行,我们定义了该扩展修改Linux内核树时应该做的工作。这是特定于linux扩展的,可以使用由foo包定义的变量,如:$(FOO_DIR)$(FOO_VERSION),以及所有linux变量,如:$(LINUX_VERSION)$(LINUX_VERSION_PROBED)$(KERNEL_ARCH),请参阅这些内核变量的定义

18.23. Hooks available in the various build steps


通用基础架构(以及派生的autotools和cmake基础架构)允许包指定钩子。这些定义了在现有步骤之后要执行的进一步操作。大多数hooks对于通用包并不是真的有用,因为.mk文件已经完全控制了包构造的每个步骤中执行的操作。

以下是可用的钩子点:

这些变量是变量名的列表,其中包含要在该挂钩点执行的操作。这允许在给定的挂钩点注册几个挂钩。下面是一个例子:

define LIBFOO_POST_PATCH_FIXUP
        action1
        action2
endef

LIBFOO_POST_PATCH_HOOKS += LIBFOO_POST_PATCH_FIXUP

18.23.1. Using the POST_RSYNC hook

POST_RSYNC钩子只在使用本地源的包中运行,无论是通过local站点方法还是通过OVERRIDE_SRCDIR机制。在这种情况下,包源会使用rsync从本地复制到buildroot构建目录中。不过,rsync命令不会复制源目录中的所有文件。属于版本控制系统的文件,如目录.git.hg等不会被复制。对于大多数包来说,这已经足够了,但是给定的包可以使用POST_RSYNC钩子执行额外的操作。

原则上,钩子可以包含任何你想要的命令。然而,一个特定的用例是使用rsync故意复制版本控制目录。你在hook中使用的rsync命令可以使用以下变量:

18.23.2. Target-finalize hook

包也可以在LIBFOO_TARGET_FINALIZE_HOOKS中注册钩子。这些钩子在构建所有包之后、生成文件系统映像之前运行。它们很少使用,您的包可能不需要它们。

18.24. Gettext integration and interaction with packages


许多支持国际化的包都使用gettext库。这个库的依赖关系相当复杂,因此需要一些解释。

glibc C库集成了一个完整的gettext实现,支持翻译。因此,glibc内置了对本地语言的支持。

另一方面,uClibc和musl C库只提供了gettext功能的存根实现,它允许使用gettext函数编译库和程序,但没有提供完整的gettext实现的转换能力。使用这样的C库,如果需要真正的本地语言支持,可以由gettext包的libintl库提供。

因此,为了确保正确处理本地语言支持,Buildroot中可以使用NLS支持的包应该:

  1. BR2_SYSTEM_ENABLE_NLS=y时,确保启用了NLS支持。对于autotools包,这是自动完成的,因此应该只对使用其他包基础架构的包进行此操作。
  2. $(TARGET_NLS_DEPENDENCIES)添加到<pkg>_DEPENDENCIES包变量中。这种添加应该无条件地完成:该变量的值由核心基础架构自动调整,以包含相关的包列表。如果禁用了NLS支持,则此变量为空。如果启用了NLS支持,此变量包含host-gettext,以便在主机上可以使用编译翻译文件所需的工具。此外,如果使用uClibc或musl,该变量还包含gettext,以便获得完整的gettext实现。
  3. 如果需要,将$(TARGET_NLS_LIBS)添加到链接标志中,以便使用libintl链接包。autotools包通常不需要这样做,因为它们通常会自动检测它们应该与libintl链接。然而,使用其他构建系统的包,或者有问题的基于autotools的包可能需要这个。$(TARGET_NLS_LIBS)应该无条件地添加到链接器标志中,因为核心会根据配置自动将其设置为空或定义为-lintl

不应该对Config.in文件进行任何更改以支持NLS。

最后,某些包在目标上需要一些gettext实用程序,例如gettext程序本身,它允许从命令行检索翻译后的字符串。在这种情况下,包应该:

18.25. Tips and tricks


18.25.1. Package name, config entry name and makefile variable relationship

在Buildroot中,以下两者之间存在某种关系:

必须使用以下规则来维护这些元素之间的一致性:

18.25.2. How to check the coding style

Buildroot在utils/check-package中提供了一个脚本,用于检查新文件或更改后的文件的编码风格。它不是一个完整的语言验证器,但它捕获了许多常见的错误。它意味着在创建提交的补丁之前,在您创建或修改的实际文件中运行。

这个脚本可以用于包,文件系统的makefile, Config.in文件等。它不检查定义包基础结构的文件和其他一些包含类似通用代码的文件。

要使用它,运行check-package脚本,告诉你创建或更改了哪些文件:

$ ./utils/check-package package/new-package/*

如果你的路径中有utils目录,你还可以运行:

$ cd package/new-package/
$ check-package *

这个工具也可以用于br2-external中的包:

$ check-package -b /path/to/br2-ext-tree/package/my-package/*

18.25.3. How to test your package

添加新包后,重要的是在各种条件下测试它:它是否为所有体系结构构建?它是否使用不同的C库构建?它需要线程吗,NPTL?等等……

Buildroot运行autobuilders,连续测试随机配置。然而,这些只会构建git树的master分支,而你的新软件包还不存在。

Buildroot在utils/test-pkg中提供了一个脚本,该脚本使用与自动构建器相同的基本配置,因此您可以在相同的条件下测试您的包。

首先,创建一个配置片段,其中包含启用包所需的所有必要选项,但不包含任何架构或工具链选项。例如,让我们创建一个配置片段,只启用libcurl,而不使用任何TLS后端:

$ cat libcurl.config
BR2_PACKAGE_LIBCURL=y

如果包需要更多配置选项,可以将它们添加到config片段中。例如,下面是你如何使用openssl作为TLS后端和curl程序来测试libcurl:

$ cat libcurl.config
BR2_PACKAGE_LIBCURL=y

然后运行test-pkg脚本,告诉它要使用哪个配置片段和要测试哪个包:

$ ./utils/test-pkg -c libcurl.config -p libcurl

默认情况下,test-pkg将根据自动构建器使用的工具链子集来构建你的包,Buildroot开发人员已将其选择为最有用和最有代表性的子集。如果你想测试所有的工具链,传入-a选项。注意,在任何情况下,内部工具链都被排除在外,因为它们的构建时间太长。

输出列出了所有测试过的工具链和相应的结果(摘录,结果是假的):

$ ./utils/test-pkg -c libcurl.config -p libcurl
                armv5-ctng-linux-gnueabi [ 1/11]: OK
              armv7-ctng-linux-gnueabihf [ 2/11]: OK
                        br-aarch64-glibc [ 3/11]: SKIPPED
                           br-arcle-hs38 [ 4/11]: SKIPPED
                            br-arm-basic [ 5/11]: FAILED
                  br-arm-cortex-a9-glibc [ 6/11]: OK
                   br-arm-cortex-a9-musl [ 7/11]: FAILED
                   br-arm-cortex-m4-full [ 8/11]: OK
                             br-arm-full [ 9/11]: OK
                    br-arm-full-nothread [10/11]: FAILED
                      br-arm-full-static [11/11]: OK
11 builds, 2 skipped, 2 build failed, 1 legal-info failed

结果的平均值为:

当出现故障时,你可以使用相同的选项重新运行脚本(在修复包之后);该脚本将尝试为所有工具链重新构建使用-p指定的包,而不需要重新构建该包的所有依赖项。

test-pkg脚本接受一些选项,你可以通过运行以下命令来获得一些帮助:

$ ./utils/test-pkg -h

18.25.4. How to add a package from GitHub

GitHub上的包通常没有包含发布压缩包的下载区。不过,可以直接从GitHub上的仓库下载压缩包。众所周知,GitHub在过去改变了下载机制,因此应该使用GitHub辅助函数,如下所示。

# Use a tag or a full commit ID
FOO_VERSION = 1.0
FOO_SITE = $(call github,<user>,<package>,v$(FOO_VERSION))

注意

如果你想添加的包在GitHub上有release部分,那么维护者可能已经上传了一个发布tarball,或者该版本可能只是从git标签中指向自动生成的tarball。如果维护者上传了一个发布tarball,我们更喜欢使用它,因为它可能略有不同(例如,它包含一个配置脚本,所以我们不需要执行AUTORECONF)。

你可以在发布页面上看到它是上传的tarball文件还是git标签:

github_hash_mongrel2.png

18.25.5. How to add a package from Gitlab

18.25.4节,“如何从GitHub添加包”中描述的github宏类似,Buildroot还提供了gitlab宏,以便从gitlab存储库中下载。它可以用于下载Gitlab生成的自动生成的压缩包,用于特定的标签或提交:

# Use a tag or a full commit ID
FOO_VERSION = 1.0
FOO_SITE = $(call gitlab,<user>,<package>,v$(FOO_VERSION))

默认情况下,它将使用.tar.gz压缩包,但Gitlab也提供了.tar.gz。因此,通过添加一个<pkg>_SOURCE变量,这个.tar.bz2压缩包可以被使用:

# Use a tag or a full commit ID
FOO_VERSION = 1.0
FOO_SITE = $(call gitlab,<user>,<package>,v$(FOO_VERSION))

如果上游开发人员在https://gitlab.com/<project>/releases/中上传了特定的压缩包,请不要使用此宏,而是直接使用到压缩包的链接。

18.26. Conclusion


如您所见,向Buildroot添加软件包只是使用现有示例编写一个Makefile,并根据软件包所需的编译过程进行修改。

如果你打包的软件可能对其他人有用,别忘了向Buildroot邮件列表发送补丁(参见第22.5节,“提交补丁”)!

Chapter 19. Patching a package


在集成新包或更新现有包时,可能需要对软件的源代码打补丁,以便在Buildroot中交叉构建。

Buildroot提供了一个基础架构,可以在构建期间自动处理此问题。它支持三种应用补丁集的方式:下载的补丁、buildroot中提供的补丁和位于用户定义的全局补丁目录中的补丁。

19.1. Providing patches


19.1.1. Downloaded

如果需要应用可下载的补丁,则将其添加到<packagename>_PATCH变量中。如果一个条目包含://,那么Buildroot将假设它是一个完整的URL并从这个位置下载补丁。否则,Buildroot将假定补丁应该从<packagename>_SITE下载。它可以是单个补丁,也可以是包含一系列补丁的压缩包。

与所有下载一样,应该将哈希值添加到<packagename>.hash文件中。

这种方法通常用于来自Debian的软件包。

19.1.2. Within Buildroot

大多数补丁都是在Buildroot的package目录中提供的。它们通常旨在修复交叉编译、libc支持或其他此类问题。

这些补丁文件应该命名为<number>-<description>.patch.

注意

19.1.3. Global patch directory

BR2_GLOBAL_PATCH_DIR配置文件选项可用于指定一个或多个包含全局包补丁的目录列表,其中由空格分隔。详情请参阅9.8节,“添加特定项目的补丁”

19.2. How patches are applied


  1. 如果定义了,运行<packagename>_PRE_PATCH_HOOKS命令;
  2. 清理构建目录,删除任何现有的*.rej文件;
  3. 如果定义了<packagename>_PATCH,则应用来自这些压缩包的补丁;
  4. 如果在包的Buildroot目录或名为<packageversion>的包子目录中有一些*.patch文件,那么:

    • 如果包目录中存在series文件,则根据series文件应用补丁;
    • 否则,匹配*.patch的补丁文件将按字母顺序应用。因此,为了确保它们按照正确的顺序应用,强烈建议将补丁文件命名为:<number>-<description>.patch,其中<number>为应用顺序。
  5. 如果定义了BR2_GLOBAL_PATCH_DIR,目录将按照指定的顺序被枚举。如上一步所述,应用补丁。
  6. 如果有定义,请运行<packagename>_POST_PATCH_HOOKS命令。

如果第3步或第4步出现问题,则构建失败。

19.3. Format and licensing of the package patches


补丁的发布与它们应用的软件遵循相同的许可证(参见13.2节,“遵守Buildroot许可证”)。

应该在补丁的首部注释中添加一条消息,解释补丁的功能以及为什么需要它。

您应该在每个补丁的头部添加一个Signed-off-by声明,以帮助跟踪更改并证明该补丁是在与所修改的软件相同的许可证下发布的。

如果软件处于版本控制状态,建议使用上游SCM软件生成补丁集。

否则,将header与diff -purN package-version.orig/ package-version/命令的输出连接起来。

如果您更新一个现有的补丁(例如,当提升软件包版本时),请确保现有的From头和Signed-off-by标记没有被删除,但要在适当的时候更新补丁注释的其余部分。

最后,补丁看起来应该像这样:

configure.ac: add C++ support test

Signed-off-by: John Doe <john.doe@noname.org>

--- configure.ac.orig
+++ configure.ac
@@ -40,2 +40,12 @@

AC_PROG_MAKE_SET
+
+AC_CACHE_CHECK([whether the C++ compiler works],
+               [rw_cv_prog_cxx_works],
+               [AC_LANG_PUSH([C++])
+                AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])],
+                               [rw_cv_prog_cxx_works=yes],
+                               [rw_cv_prog_cxx_works=no])
+                AC_LANG_POP([C++])])
+
+AM_CONDITIONAL([CXX_WORKS], [test "x$rw_cv_prog_cxx_works" = "xyes"])

19.4. Additional patch documentation


理想情况下,所有补丁都应通过“上游”预告片记录上游补丁或补丁提交(如果适用)。

在向主线移植已经被接受的upstream补丁时,最好引用提交的URL:

Upstream: <URL to upstream commit>

如果在Buildroot中发现了一个新问题,并且upstream通常受到该问题的影响(这不是Buildroot特有的问题),用户应该提交补丁upstream,并在可能的情况下提供该提交的链接:

Upstream: <URL to upstream mailing list submission or merge request>

已经提交但被上游拒绝的补丁应注意这一点,并包括评论,说明尽管处于上游状态,该补丁仍被使用的原因。

注意:在上述任何场景中,添加几句话,说明可能需要对补丁进行的任何更改,也是明智的。

如果一个补丁没有在上游应用,那么应该注释说明:

Upstream: <URL to upstream mailing list submission or merge request>

添加此文档有助于简化包版本更新期间的补丁审查过程。

Chapter 20. Download infrastructure


TODO

Chapter 21. Debugging Buildroot


在构建包时,可以检测Buildroot的步骤。定义变量BR2_INSTRUMENTATION_SCRIPTS,在空格分隔的列表中包含一个或多个脚本(或其他可执行文件)的路径,你想在每个步骤之前和之后调用它。这些脚本按顺序调用,有三个参数:

例如:

make BR2_INSTRUMENTATION_SCRIPTS="/path/to/my/script1 /path/to/my/script2"

步骤如下:

该脚本可以访问以下变量:

Chapter 22. Contributing to Buildroot


您可以通过多种方式为Buildroot做出贡献:分析和修复bug,分析和修复由自动构建器检测到的包构建失败,测试和审查其他开发人员发送的补丁,按照我们的待办事项列表中的项目工作,并将您自己的改进发送到Buildroot或其手册。以下各节将对这些项目进行更详细的介绍。

如果您有兴趣为Buildroot做出贡献,您应该做的第一件事是订阅Buildroot邮件列表。这个列表是与其他Buildroot开发人员交互和贡献的主要方式。如果你还没有订阅,请参阅Chapter 5, Community resources的订阅链接。

如果你要接触代码,强烈建议使用git的Buildroot仓库,而不是从提取的源代码tar包开始。Git是开发并直接将补丁发送到邮件列表的最简单方法。请参阅第3章,使用Buildroot以获取有关获取Buildroot git树的更多信息。

22.1. Reproducing, analyzing and fixing bugs


贡献的第一种方式是查看Buildroot bug tracker中的开放bug报告。当我们努力保持尽可能少的bug数量时,所有对重现、分析和修复bug的帮助都是非常受欢迎的。即使你还没有看到完整的情况,也不要犹豫,在报告发现的bug报告中添加注释。

22.2. Analyzing and fixing autobuild failures


Buildroot autobuilders是一组基于随机配置持续运行Buildroot构建的构建机器。这适用于Buildroot支持的所有体系结构、各种工具链和随机选择的包。在Buildroot上有大量的提交活动,这些自动构建器对于在提交后尽早检测问题有很大帮助。

所有构建结果可在http://autobuild.buildroot.org获得,统计数据可在http://autobuild.buildroot.org/stats.php获得。每天,所有失败包的概述都会发送到邮件列表。

发现问题是很好的,但显然这些问题也必须被修复。非常欢迎您的贡献!基本上可以做两件事:

Fixes: http://autobuild.buildroot.org/results/51000a9d4656afe9e0ea6f07b9f8ed374c2e4069

22.3. Reviewing and testing patches


随着每天发送到邮件列表的补丁数量的增加,维护人员很难判断哪些补丁可以应用,哪些不能。贡献者可以通过检查和测试这些补丁来提供很大的帮助。

在评审过程中,不要犹豫地回复补丁提交的评论、建议或任何有助于大家理解补丁并使其更好的东西。请使用互联网风格的回复在回复补丁提交的纯文本电子邮件。

为了表示一个补丁的批准,有三个正式的标签来跟踪这个批准。要将您的标签添加到补丁中,请在原始作者的签名行下面回复批准标签。这些标记将由patchwork自动获取(参见第22.3.1节,“从Patchwork中应用补丁”),并在补丁被接受时成为提交日志的一部分。

Tested-by

表示补丁测试成功。我们鼓励您指定您执行的测试类型(架构X和Y上的编译测试,目标A上的运行时测试,…)。这些额外的信息可以帮助其他测试人员和维护人员。

Reviewed-by

表示您审阅了该补丁的代码,并尽了最大努力来发现问题,但您对涉及的领域还不够熟悉,无法提供Acked-by标签。这意味着补丁中可能还有遗留的问题,会被在该领域有更多经验的人发现。如果发现此类问题,您的Reviewed-by标签仍然是合适的,您不会受到指责。

Acked-by

表示您审阅了该补丁的代码,并且您对涉及的领域足够熟悉,可以认为该补丁可以原样提交(不需要额外的更改)。万一后来发现补丁有问题,你的Acked-by可能会被认为是不合适的。因此,受攻击的补丁和受检查的补丁之间的区别主要在于,你准备好接受Acked补丁的责任,而不是经过检查的补丁。

如果您审阅了一个补丁并对其发表了评论,您应该简单地回复该补丁并说明这些评论,而不是提供一个Reviewed-by 或 Acked-by标签。只有当你判断补丁是好的时候,才应该提供这些标签。

值得注意的是,审查或攻击都不意味着已经执行了测试。要表明您已经审阅和测试了补丁,请提供两个单独的标签(Reviewed/Acked-by 和 Tested-by)。

还要注意,任何开发人员都可以提供Tested/Reviewed/Acked-by的标签,无一例外,我们鼓励每个人都这样做。Buildroot没有明确的核心开发人员组,只是碰巧有些开发人员比其他开发人员更活跃。维护者会根据提交者的跟踪记录来评估标签的价值。与新手提供的标签相比,普通贡献者提供的标签自然更值得信任。随着您更定期地提供标签,您的可信性(在维护者看来)将提高,但提供的任何标签都是有价值的。

Buildroot的Patchwork网站可以用于引入补丁以进行测试。有关使用Buildroot的Patchwork网站应用补丁的更多信息,请参阅第22.3.1节,“从Patchwork应用补丁”

22.3.1. Applying Patches from Patchwork

对于开发者来说,Buildroot的Patchwork网站的主要用途是将补丁导入本地git仓库以进行测试。

当在补丁管理界面中浏览补丁时,在页面顶部会提供一个mbox链接。复制此链接地址并运行以下命令:

$ git checkout -b <test-branch-name>
$ wget -O - <mbox-url> | git am

应用补丁的另一个选择是创建一个bundle。bundle是一组补丁,你可以使用patchwork接口将它们组合在一起。一旦打包文件被创建并被公开,你可以复制打包文件的mbox链接并使用上述命令应用打包文件。

22.4. Work on items from the TODO list


如果您想为Buildroot做出贡献,但不知道从哪里开始,并且您不喜欢上述任何主题,您总是可以处理Buildroot待办事项清单中的项目。请不要犹豫,首先在邮件列表或IRC上讨论某个项目。一定要编辑wiki以指示您何时开始处理一个项目,这样我们就可以避免重复工作。

22.5. Submitting patches


注意

请不要将补丁附加到bug上,而是将它们发送到邮件列表中。

如果您对Buildroot进行了一些更改,并且希望将它们贡献到Buildroot项目中,请按照以下步骤进行操作。

22.5.1. The formatting of a patch

我们希望补丁以特定的方式格式化。这是必要的,可以方便地审查补丁,方便地将它们应用于git仓库,方便地查找历史上发生更改的方式和原因,并使使用git bisect来定位问题的来源成为可能。

首先,补丁必须有一个良好的提交消息。提交消息应该以单独的一行开始,一行是改动的简要摘要,以补丁涉及的区域作为前缀。一些好的提交标题的例子:

前缀后面的描述应以小写字母开头(即上面示例中的“bump”、“needs”、“postpone”、“add”)。

其次,提交消息的主体应该描述为什么需要进行此更改,如果有必要,还应该给出如何进行更改的详细信息。在编写提交消息时,不仅要考虑评审人员如何阅读它,还要考虑几年后你再次看到这个更改时,你将如何阅读它。

第三,补丁本身应该只做一项更改,但要完成全部更改。两个不相关或弱相关的更改通常应该在两个独立的补丁中完成。这通常意味着一个补丁只影响一个包。如果涉及多个更改,通常仍然可以将它们分成小的补丁,并按特定的顺序应用。小的补丁更容易评审,通常也更容易理解为什么要进行更改。但每个补丁必须是完整的。如果只打了第一个补丁而没有打第二个补丁,则不允许构建失败。这对于之后使用git bisect是必要的。

当然,当你进行开发时,你可能会在包之间来回切换,当然不会立即以足够干净的方式提交内容。因此,大多数开发人员都会重写提交的历史记录,以生成一组干净的、适合提交的提交。要做到这一点,你需要使用交互式变基。你可以在在Pro Git book中中了解它。有时,使用git reset --soft origin/master命令丢弃你的历史记录,并使用git add -igit add -p选择单独的更改更容易。

最后,应该对补丁进行签名。这可以通过在提交消息的末尾添加Signed-off-by: Your Real Name <your@email.address>来实现。如果配置正确,git commit -s会帮你完成这个任务。Signed-off-by标记意味着您可以在Buildroot许可证下发布补丁(即GPL-2.0+,但包补丁除外,它们具有上游许可证),并且您可以这样做。详见开发者原产地证书

当添加新包时,你应该在单独的补丁中提交每个包。这个补丁应该更新到package/Config.inConfig.in包文件、.mk文件、.hash文件、任何初始化脚本和所有包补丁。如果包有许多子选项,有时最好将它们作为单独的后续补丁添加。摘要行应该类似于<packagename>: new package。对于简单的包,提交消息的主体可以是空的,也可以包含包的描述(如Configin帮助文本)。如果必须做任何特殊的事情来构建包,也应该在commit消息体中明确解释。

当你把一个包升级到新版本时,你还应该为每个包提交一个单独的补丁。不要忘记更新.hash文件,如果它还不存在,请添加它。另外,不要忘记检查_LICENSE_LICENSE_FILES是否仍然有效。摘要行应该类似于<packagename>: bump to version <new version>。如果新版本只包含与现有版本相比的安全更新,摘要应该是<packagename>: security bump to version <new version>,并且提交消息体应该显示已固定的CVE编号。如果一些包补丁可以在新版本中删除,则应该明确解释为什么可以删除它们,最好使用上游提交ID。此外,任何其他需要的更改都应该明确说明,例如不再存在或不再需要的配置选项。

如果您有兴趣获得构建失败的通知以及您添加或修改的包的进一步更改,请将自己添加到开发者文件中。这应该在创建或修改包的同一个补丁中完成。更多信息请参阅开发者文件

Buildroot提供了一个方便的工具来检查你创建或修改的文件中常见的编码风格错误,名为check-package(更多信息请参见18.25.2节,“如何检查编码风格”)。

22.5.2. Preparing a patch series

从本地git视图中提交的更改开始,在生成补丁集之前,将开发分支重设到上游树的顶部。为此,运行:

$ git fetch --all --tags
$ git rebase origin/master

现在检查你提交的代码样式:

$ utils/docker-run make check-package

现在,您已经准备好生成并提交补丁集。

要生成它,运行:

$ git format-patch -M -n -s -o outgoing origin/master

这将在outgoing子目录中生成补丁文件,并自动添加Signed-off-by行。

生成补丁文件后,你可以使用你喜欢的文本编辑器在提交之前审查/编辑提交信息。

Buildroot提供了一个方便的工具来知道你的补丁应该发送给谁,名为get-developers(更多信息请参阅第23章,DEVELOPERS文件和get-developers)。这个工具读取你的补丁并输出适当的git send-email命令来使用:

$ ./utils/get-developers outgoing/*

使用get-developers的输出来发送你的补丁:

$ git send-email --to buildroot@buildroot.org --cc bob --cc alice outgoing/*

或者,get-developers -e可以直接与git send-email--cc-cmd参数一起使用,自动抄送受影响的开发者:

$ git send-email --to buildroot@buildroot.org \
      --cc-cmd './utils/get-developers -e' origin/master

git可以配置为自动执行此操作:

$ git config sendemail.to buildroot@buildroot.org
$ git config sendemail.ccCmd "$(pwd)/utils/get-developers -e"

然后只需要做:

$ git send-email origin/master

注意,git应该配置为使用你的邮件账户。要配置git,请参阅man git-send-email或谷歌它。

如果你不使用git send-email,请确保发布的补丁不换行,否则它们将无法轻松应用。在这种情况下,修复你的电子邮件客户端,或者更好的是,学习使用git send-email

22.5.3. Cover letter

如果你想在单独的邮件中展示整个补丁集,请在git format-patch命令中添加--cover-letter(有关更多信息,请参阅man git-format-patch)。这将为您的补丁系列的介绍电子邮件生成一个模板。

在以下情况下,求职信可以介绍你提出的修改建议:

22.5.4. Patches for maintenance branches

当在维护分支上修复bug时,应该先在主分支上修复bug。该补丁的提交日志可能会包含一个提交后的说明,说明受影响的分支:

package/foo: fix stuff

Signed-off-by: Your Real Name <your@email.address>
---
Backport to: 2020.02.x, 2020.05.x
(2020.08.x not affected as the version was bumped)

这些更改将由维护者向后移植到受影响的分支。

然而,有些bug可能只适用于特定的版本,例如,因为它使用的是较旧版本的包。在这种情况下,补丁应该基于维护分支,而补丁主题前缀必须包括维护分支的名称(例如”[patch 2020.02.x]”)。这可以通过git format-patch标志--subject-prefix来实现:

$ git format-patch --subject-prefix "PATCH 2020.02.x" \
    -M -s -o outgoing origin/2020.02.x

然后使用git send-email发送补丁,如上所述。

22.5.5. Patch revision changelog

当要求改进时,每次提交的新版本应该包括每次提交之间修改的更改日志。请注意,如果你的补丁系列是通过求职信介绍的,除了每次提交的更改日志外,还可以在求职信中添加一个整体的更改日志。重写补丁系列的最佳方式是通过交互式变基:git rebase -i origin/master。有关更多信息,请参阅git手册。

当添加到单个提交时,此更改日志会在编辑提交消息时添加。在Signed-off-by部分下面,添加---和你的变更日志。

尽管审阅者在邮件线程以及patchwork中都可以看到更改日志,但当补丁将被合并时,git将自动忽略---下面的行。这是预期的行为:更改日志并不意味着永远保存在项目的git历史记录中。

以下为推荐布局:

Patch title: short explanation, max 72 chars

A paragraph that explains the problem, and how it manifests itself. If
the problem is complex, it is OK to add more paragraphs. All paragraphs
should be wrapped at 72 characters.

A paragraph that explains the root cause of the problem. Again, more
than one paragraph is OK.

Finally, one or more paragraphs that explain how the problem is solved.
Don't hesitate to explain complex solutions in detail.

Signed-off-by: John DOE <john.doe@example.net>

---
Changes v2 -> v3:
  - foo bar  (suggested by Jane)
  - bar buz

Changes v1 -> v2:
  - alpha bravo  (suggested by John)
  - charly delta

任何补丁版本都应该包含版本号。版本号简单地由字母v后跟一个大于或等于2的integer组成(即:”PATCH v2”, “PATCH v3”…)

这可以通过使用git format-patch选项--subject-prefix轻松处理:

$ git format-patch --subject-prefix "PATCH v4" \
    -M -s -o outgoing origin/master

从git 1.8.1版本开始,你可以使用-v <n>(其中是版本号):

$ git format-patch -v4 -M -s -o outgoing origin/master

当您提供补丁的新版本时,请在patchwork中标记旧版本为已取代。你需要在patchwork上创建一个帐户,以便能够修改补丁的状态。请注意,您只能更改您自己提交的补丁的状态,这意味着您在patchwork中注册的电子邮件地址应该与您用于向邮件列表发送补丁的电子邮件地址相匹配。

在向邮件列表提交补丁时,你也可以添加--in-reply-to <message-id>选项。要回复的邮件id可以在patchwork的”Message Id”标签下找到。in-reply-to的优点是patchwork会自动将前一个版本的补丁标记为已取代。

22.6. Reporting issues/bugs or getting help


在报告任何问题之前,请检查邮件列表归档是否有人已经报告和/或修复了类似的问题。

无论你选择报告bug还是获得帮助,无论是通过在bug tracker中打开一个bug,还是通过向邮件列表发送邮件,都有很多细节可以提供,以帮助人们重现问题并找到解决方案。

试着把自己想象成是在帮助别人;那样的话,你需要什么?

以下是在这种情况下提供的详细信息:

此外,你应该添加.config文件(或者如果你知道怎么做,一个defconfig;参见9.3节,“存储Buildroot配置”)。

如果这些细节太大,不要犹豫,使用pastebin服务。请注意,并不是所有可用的pastebin服务在下载原始粘贴时都会保留unix风格的行终止符。以下pastebin服务是已知的正确工作:- https://gist.github.com/ - http://code.bulix.org/

22.7. Using the runtime tests framework


Buildroot包含一个基于Python脚本和QEMU运行时执行的运行时测试框架。该框架的目标如下。

使用运行时测试框架的入口点是support/testing/run-tests工具,它在工具的帮助-h描述中记录了一系列选项。一些常见的选项包括设置下载文件夹、输出文件夹、保留构建输出,对于多个测试用例,您可以为每个测试用例设置JLEVEL。

下面是运行测试用例的示例。

$ support/testing/run-tests -l
List of tests
test_run (tests.utils.test_check_package.TestCheckPackage)
test_run (tests.toolchain.test_external.TestExternalToolchainBuildrootMusl) ... ok
test_run (tests.toolchain.test_external.TestExternalToolchainBuildrootuClibc) ... ok
test_run (tests.toolchain.test_external.TestExternalToolchainCCache) ... ok
test_run (tests.toolchain.test_external.TestExternalToolchainCtngMusl) ... ok
test_run (tests.toolchain.test_external.TestExternalToolchainLinaroArm) ... ok
test_run (tests.toolchain.test_external.TestExternalToolchainSourceryArmv4) ... ok
test_run (tests.toolchain.test_external.TestExternalToolchainSourceryArmv5) ... ok
test_run (tests.toolchain.test_external.TestExternalToolchainSourceryArmv7) ... ok
[snip]
test_run (tests.init.test_systemd.TestInitSystemSystemdRoFull) ... ok
test_run (tests.init.test_systemd.TestInitSystemSystemdRoIfupdown) ... ok
test_run (tests.init.test_systemd.TestInitSystemSystemdRoNetworkd) ... ok
test_run (tests.init.test_systemd.TestInitSystemSystemdRwFull) ... ok
test_run (tests.init.test_systemd.TestInitSystemSystemdRwIfupdown) ... ok
test_run (tests.init.test_systemd.TestInitSystemSystemdRwNetworkd) ... ok
test_run (tests.init.test_busybox.TestInitSystemBusyboxRo) ... ok
test_run (tests.init.test_busybox.TestInitSystemBusyboxRoNet) ... ok
test_run (tests.init.test_busybox.TestInitSystemBusyboxRw) ... ok
test_run (tests.init.test_busybox.TestInitSystemBusyboxRwNet) ... ok

Ran 157 tests in 0.021s

OK
$ support/testing/run-tests -d dl -o output_folder -k tests.init.test_busybox.TestInitSystemBusyboxRw
15:03:26 TestInitSystemBusyboxRw                  Starting
15:03:28 TestInitSystemBusyboxRw                  Building
15:08:18 TestInitSystemBusyboxRw                  Building done
15:08:27 TestInitSystemBusyboxRw                  Cleaning up
.
Ran 1 test in 301.140s

OK

标准输出表示测试是否成功。默认情况下,测试的输出文件夹将自动删除,除非将-k选项传递给keep输出目录。

22.7.1. Creating a test case

在Buildroot仓库中,测试框架通过conf, infratests文件夹组织在顶层的support/testing/中。所有的测试用例都在tests文件夹下,并被组织在代表测试类别的不同文件夹中。

熟悉如何创建测试用例的最好方法是看一些基本的文件系统support/testing/tests/fs/support/testing/tests/init/测试脚本。这些测试很好地展示了基本测试的例子,包括检查构建结果和执行运行时测试。还有其他更高级的情况,使用嵌套的br2-external文件夹来提供骨架和额外的包。

创建一个基本的测试用例包括:

在创建测试脚本之后,将自己添加到DEVELOPERS文件中,成为测试用例的维护者。

22.7.2. Debugging a test case

当一个测试用例运行时,output_folder将包含以下内容:

$ ls output_folder/
TestInitSystemBusyboxRw/
TestInitSystemBusyboxRw-build.log
TestInitSystemBusyboxRw-run.log

TestInitSystemBusyboxRw/ 是Buildroot的输出目录,只有在传递了-k选项时,它才会被保留。

TestInitSystemBusyboxRw-build.log 是Buildroot构建的日志。

TestInitSystemBusyboxRw-run.log 是Qemu启动和测试的日志。仅当构建成功且测试用例涉及在Qemu下启动时,此文件才会存在。

如果你想手动运行Qemu对构建结果进行手动测试,TestInitSystemBusyboxRw-run.log的前几行包含了要使用的Qemu命令行。

您还可以对output_folder中的当前源代码进行修改(例如,用于调试目的),并重新运行标准的Buildroot make目标(以便重新生成具有新修改的完整图像),然后重新运行测试。

22.7.3. Runtime tests and Gitlab CI

所有运行时测试都由Buildroot Gitlab CI基础架构定期执行,请参阅.gitlab.yml和https://gitlab.com/buildroot.org/buildroot/-/jobs

您还可以使用Gitlab CI来测试新的测试用例,或者验证在Buildroot中进行更改后现有测试是否继续工作。

为了实现这一点,你需要在Gitlab上创建一个Buildroot项目的分支,并能够将分支推送到你在Gitlab上的Buildroot分支。

您推送的分支的名称将决定是否会触发Gitlab CI管道,以及针对哪些测试用例。

在下面的例子中,组件的分支名称是您选择的任意字符串。

$ git push gitlab HEAD:<name>-runtime-tests
$ git push gitlab HEAD:<name>-<test case name>

运行一个测试的例子:

$ git push gitlab HEAD:foo-tests.init.test_busybox.TestInitSystemBusyboxRo

运行同一组中多个测试的示例:

$ git push gitlab HEAD:foo-tests.init.test_busybox
$ git push gitlab HEAD:foo-tests.init

[4] RFC: (Request for comments) 更改建议

Chapter 23. DEVELOPERS file and get-developers


主Buildroot目录包含一个名为DEVELOPERS的文件,该文件列出了涉及Buildroot各个领域的开发人员。多亏了这个文件,get-developers工具允许:

我们要求在Buildroot中添加新包、新板或新功能的开发人员在 DEVELOPERS文件中注册自己。例如,我们希望开发人员贡献一个新包,在他的补丁中包含对DEVELOPERS文件的适当修改。

DEVELOPERS文件格式在文件本身中有详细的文档说明。

位于utils/目录下的get-developers工具允许我们将DEVELOPERS文件用于各种任务:

Chapter 24. Release Engineering


24.1. Releases


Buildroot项目每季度发布一次,每月发布一次bug修复版本。每年的第一个版本是一个长期支持版本LTS。

该版本支持到下一个版本的第一个bug修复版本,例如,2020.05.x在2020.08.1发布时是EOL。

支持LTS版本,直到下一个LTS的第一个bug修复版本发布,例如2020.02.x,直到2021.02.1发布。

24.2. Development


每个发布周期包括两个月的“主”分支开发和发布前一个月的稳定。在此阶段,没有新功能添加到master,只有bug修复。

稳定阶段从标记-rc1开始,直到发布之前,每周都会标记另一个候选发布版本。

为了处理稳定阶段的新功能和版本变化,可以为这些功能创建一个next分支。一旦创建了当前版本,next分支将被合并到master分支中,并继续进行下一个版本的开发周期。

Part IV. Appendix

Chapter 25. Makedev syntax documentation


makedev语法在Buildroot的几个地方被用来定义权限要做的更改,或者创建哪些设备文件以及如何创建它们,以避免调用mknod。

这种语法源自makedev实用工具,更完整的文档可以在package/makedevs/README文件中找到。

它采用空格分隔字段列表的形式,每行一个文件;这些字段包括:

                   
name type mode uid gid major minor start inc count

以下是一些重要的块。

假设你想要改变一个给定文件的权限;使用这种语法,你需要编写:

/usr/bin/foo f 755 0 0 - - - - -
/usr/bin/bar f 755 root root - - - - -
/data/buz f 644 buz-user buz-group - - - - -

或者,如果您想递归地更改目录的所有者/权限,您可以编写(将UID设置为foo, GID设置为bar,并将访问权限设置为rwxr-x—对于目录/usr/share/myapp和它下面的所有文件和目录):

/usr/share/myapp r 750 foo bar - - - - -

另一方面,如果您想为分区创建设备文件/dev/hda和相应的15个文件,则需要/dev/hda:

/dev/hda b 640 root root 3 0 0 0 -

然后对于/dev/hda, /dev/hdaX, X中1到15之间的分区对应的设备文件:

/dev/hda b 640 root root 3 1 1 1 15

如果启用了BR2_ROOTFS_DEVICE_TABLE_SUPPORTS_EXTENDED_ATTRIBUTES,则支持扩展属性。这是通过在描述文件的行之后添加以|xattr开头的行来实现的。目前,只有capability作为扩展属性被支持。

   
xattr capability

如果你想把cap_sys_admin能力添加到二进制文件foo中,你可以这样写:

/usr/bin/foo f 755 root root - - - - -
|xattr cap_sys_admin+eip

您可以通过使用多个|xattr行来向文件添加多个功能。如果你想把cap_sys_admin和cap_net_admin能力添加到二进制文件foo中,你可以这样写:

/usr/bin/foo f 755 root root - - - - -
|xattr cap_sys_admin+eip
|xattr cap_net_admin+eip

Chapter 26. Makeusers syntax documentation


创建用户的语法受上面的makedev语法的启发,但特定于Buildroot。

添加用户的语法是用空格分隔的字段列表,每行一个用户;这些字段包括:

                 
username uid group gid password home shell groups comment

地点:

对每个字段的内容有一些限制。

如果home不是-,那么主目录和下面的所有文件都将属于用户及其主组。

示例:

foo -1 bar -1 !=blabla /home/foo /bin/sh alpha,bravo Foo user

这将创建这个用户:

test 8000 wheel -1 = - /bin/sh - Test user

这将创建这个用户:

26.1. Caveat with automatic UIDs and GIDs


当更新buildroot或在配置中添加或删除包时,自动UID和GID可能会发生更改。如果持久化文件是由该用户或组创建的,这可能会出现问题:升级后,它们会突然有不同的所有者。

因此,建议保留自动ID。这可以通过添加一个包含生成的ID的users表来实现。它只需要为实际创建持久文件的UID执行此操作,例如数据库。

Chapter 27. Migrating from older Buildroot versions


有些版本引入了向后不兼容性。本节将解释这些不兼容性,并针对每个不兼容性说明如何完成迁移。

27.1. General approach


要从旧的Buildroot版本迁移,请执行以下步骤。

  1. 对于所有配置,请在旧的Buildroot环境中执行构建。运行make graph-size。将graphs/file-size-stats.csv保存到不同的位置。运行make clean删除其余的。
  2. 请查看下面的迁移说明,并对外部包和自定义构建脚本进行必要的调整。
  3. 更新Buildroot。
  4. 从现有的.config运行make menuconfig
  5. 如果在Legacy菜单中启用了任何功能,请选中它的帮助文本,取消选中它,并保存配置。
  6. 要了解更多细节,请查看git提交消息,查找你需要的包。切换到packages目录并运行git log <old version>.. — <your packages>
  7. 在新的Buildroot环境中构建。
  8. 修复外部包的构建问题(通常是由于更新的依赖项)。
  9. 运行make graph-size.
  10. 将新的file-size-stats.csv与原始文件进行比较,检查是否没有必需的文件消失,是否没有新的不需要的大文件出现。
  11. 对于覆盖Buildroot创建的文件的自定义覆盖中的配置(和其他)文件,请检查Buildroot生成的文件中是否有需要传播到您的自定义文件中的更改。

27.2. Migrating to 2016.11


在Buildroot 2016.11之前,一次只能使用一个br2-external树。随着Buildroot 2016.11的出现,可以同时使用多个自定义项(详情请参见9.2节,“在Buildroot之外保留自定义”)。

但这意味着较旧的br2-external树不能按原样使用。需要做一个小的更改:向br2-external树添加一个名称。

这可以通过几个步骤非常简单地完成:

$ echo 'name: NAME_OF_YOUR_TREE' >external.desc

注意. 在选择名称时要小心:它必须是唯一的,并且只能由集合[A-Za-z0-9_]中的ASCII字符组成。

$ find . -type f | xargs sed -i 's/BR2_EXTERNAL/BR2_EXTERNAL_NAME_OF_YOUR_TREE_PATH/g'

现在,你的br2-external树可以与Buildroot 2016.11一起使用。

注意: 这一更改使您的br2-external树在2016.11之前与Buildroot不兼容。

27.3. Migrating to 2017.08


在Buildroot 2017.08之前,主机包安装在$(HOST_DIR)/usr中(例如autotools --prefix=$(HOST_DIR)/usr)。在Buildroot 2017.08中,它们现在直接安装在$(HOST_DIR)中。

每当一个包安装了一个与$(HOST_DIR)/lib中的库链接的可执行文件时,它必须有一个RPATH指向该目录。

指向$(HOST_DIR)/usr/lib的RPATH不再被接受。