---
## 场景设定
我们要为嵌入式系统添加一个名为 `hello-buildroot` 的应用,它会向控制台打印 "Hello from Buildroot!"。
### 项目结构
```
~/my_project/
├── buildroot/ # Buildroot源码树
└── app_src/ # 外部应用源码
└── hello-buildroot/
├── hello.c
├── Makefile
└── README
```
---
## 第一阶段:准备应用源码
### 1. 创建应用源码
**创建应用目录:**
```bash
mkdir -p ~/my_project/app_src/hello-buildroot
cd ~/my_project/app_src/hello-buildroot
```
**编写 `hello.c`:**
```c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
int count = 0;
printf("Hello from Buildroot!\n");
printf("Application running on: ");
// 打印系统信息
#ifdef __linux__
printf("Linux");
#endif
#ifdef __arm__
printf(" (ARM architecture)");
#endif
#ifdef __x86_64__
printf(" (x86_64 architecture)");
#endif
printf("\n\n");
// 每秒打印一次当前计数,演示程序在运行
while(1) {
printf("Count: %d\n", count++);
sleep(1);
}
return 0;
}
```
**编写 `Makefile`:**
```makefile
# 交叉编译工具链变量将由Buildroot自动传入
CC = $(TARGET_CC)
CFLAGS = -Wall -O2
LDFLAGS =
TARGET = hello-buildroot
SOURCES = hello.c
OBJECTS = $(SOURCES:.c=.o)
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJECTS) $(TARGET)
install:
# 安装到目标系统(由Buildroot的INSTALL_TARGET_CMDS处理)
@echo "Manual install handled by Buildroot"
.PHONY: all clean install
```
---
## 第二阶段:创建Buildroot软件包
### 2. 创建包目录结构
```bash
cd ~/my_project/buildroot
mkdir -p package/hello-buildroot
```
### 3. 编写 `Config.in`
在 `package/hello-buildroot/Config.in` 中添加:
```kconfig
config BR2_PACKAGE_HELLO_BUILDROOT
bool "hello-buildroot"
help
A simple demo application that prints "Hello from Buildroot!"
This package demonstrates how to add a custom application
to the Buildroot system.
https://example.com/hello-buildroot
```
### 4. 编写 `hello-buildroot.mk`
这是最关键的文件,定义如何构建和安装应用。
```makefile
################################################################################
# hello-buildroot
################################################################################
# 版本号(可以是日期、git hash或自定义版本)
HELLO_BUILDROOT_VERSION = 1.0.0
# 源码位置:使用本地源码(外部目录)
HELLO_BUILDROOT_SITE = $(TOPDIR)/../app_src/hello-buildroot
HELLO_BUILDROOT_SITE_METHOD = local
# 许可证信息
HELLO_BUILDROOT_LICENSE = MIT
HELLO_BUILDROOT_LICENSE_FILES = README
# 依赖项(如果有的话)
# HELLO_BUILDROOT_DEPENDENCIES = libfoo libbar
# 定义构建命令
define HELLO_BUILDROOT_BUILD_CMDS
# $(@D) 是解压后的源码目录
# 使用Buildroot的交叉编译工具链
$(MAKE) CC=$(TARGET_CC) LD=$(TARGET_LD) \
CFLAGS="$(TARGET_CFLAGS)" \
LDFLAGS="$(TARGET_LDFLAGS)" \
-C $(@D) all
endef
# 定义安装到目标系统的命令
define HELLO_BUILDROOT_INSTALL_TARGET_CMDS
# 创建目标目录(如果不存在)
mkdir -p $(TARGET_DIR)/usr/bin
# 安装二进制文件
$(INSTALL) -D -m 0755 $(@D)/hello-buildroot $(TARGET_DIR)/usr/bin/hello-buildroot
# 可选:创建配置文件或数据文件
# mkdir -p $(TARGET_DIR)/etc/hello
# $(INSTALL) -D -m 0644 $(@D)/hello.conf $(TARGET_DIR)/etc/hello/hello.conf
endef
# 可选:安装到 staging(用于开发头文件/库)
# define HELLO_BUILDROOT_INSTALL_STAGING_CMDS
# $(INSTALL) -D -m 0644 $(@D)/hello.h $(STAGING_DIR)/usr/include/hello.h
# endef
# 注册包类型(generic是最通用的)
$(eval $(generic-package))
```
### 5. 将包注册到Buildroot主配置
编辑 `package/Config.in`,添加你的包:
```bash
# 在合适位置添加,比如在 "Custom packages" 菜单下
vi package/Config.in
```
找到合适位置(例如文件末尾)添加:
```kconfig
menu "Custom packages"
source "package/hello-buildroot/Config.in"
endmenu
```
---
## 第三阶段:构建系统
### 6. 配置Buildroot
进入Buildroot配置界面:
```bash
cd ~/my_project/buildroot
# 如果没有配置过,先选择目标平台(以QEMU ARM为例)
make qemu_arm_vexpress_defconfig
# 打开menuconfig选择你的应用
make menuconfig
```
在menuconfig中导航到:
```
Target packages --->
Custom packages --->
[*] hello-buildroot
```
保存并退出。
### 7. 构建整个系统
```bash
# 开始构建(首次会比较慢,因为要下载很多依赖)
make
```
构建完成后,输出文件在 `output/images/`。
---
## 第四阶段:验证安装
### 8. 检查安装结果
**查看构建日志:**
```bash
# 查看是否编译了hello-buildroot
grep "hello-buildroot" output/build/build-time.log
# 查看安装的文件
ls -la output/target/usr/bin/hello-buildroot
file output/target/usr/bin/hello-buildroot
```
**查看根文件系统镜像内容:**
```bash
# 如果使用ext4格式
mkdir -p /tmp/rootfs_check
sudo mount -o loop output/images/rootfs.ext4 /tmp/rootfs_check
ls -la /tmp/rootfs_check/usr/bin/hello-buildroot
sudo umount /tmp/rootfs_check
```
**查看最终的镜像大小:**
```bash
ls -lh output/images/rootfs.*
```
---
## 第五阶段:测试运行
### 9. 在QEMU中测试(假设使用ARM平台)
```bash
# 启动QEMU模拟器
qemu-system-arm -M vexpress-a9 \
-kernel output/images/zImage \
-dtb output/images/vexpress-v2p-ca9.dtb \
-append "root=/dev/mmcblk0 console=ttyAMA0" \
-drive file=output/images/rootfs.ext4,if=sd,format=raw \
-nographic
# 在QEMU中登录后运行应用
# 用户名: root (无密码)
/usr/bin/hello-buildroot
```
预期输出:
```
Hello from Buildroot!
Application running on: Linux (ARM architecture)
Count: 0
Count: 1
Count: 2
...
```
---
## 第六阶段:调试与迭代
### 10. 修改并重新编译应用
当需要修改应用时:
```bash
# 1. 修改源码
vi ~/my_project/app_src/hello-buildroot/hello.c
# 2. 只重新编译你的应用(不重建整个系统)
cd ~/my_project/buildroot
rm -rf output/build/hello-buildroot-*
make hello-buildroot
# 3. 重新生成根文件系统镜像
make ext4-rootfs
# 4. 重新测试
qemu-system-arm ... (同上)
```
---
## 完整脚本:一键添加应用
为了简化过程,这里提供一个自动化脚本:
```bash
#!/bin/bash
# 文件名: add-custom-app.sh
APP_NAME="hello-buildroot"
APP_SRC_DIR="$HOME/my_project/app_src/$APP_NAME"
BR_DIR="$HOME/my_project/buildroot"
echo "Step 1: Create application source..."
mkdir -p "$APP_SRC_DIR"
cat > "$APP_SRC_DIR/hello.c" << 'EOF'
#include <stdio.h>
int main() {
printf("Hello from Buildroot!\n");
return 0;
}
EOF
cat > "$APP_SRC_DIR/Makefile" << 'EOF'
CC = $(TARGET_CC)
CFLAGS = -Wall -O2
TARGET = hello-buildroot
SOURCES = hello.c
OBJECTS = $(SOURCES:.c=.o)
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(CFLAGS) -o $@ $^
clean:
rm -f $(OBJECTS) $(TARGET)
.PHONY: all clean
EOF
echo "Step 2: Create Buildroot package..."
mkdir -p "$BR_DIR/package/$APP_NAME"
cat > "$BR_DIR/package/$APP_NAME/Config.in" << EOF
config BR2_PACKAGE_HELLO_BUILDROOT
bool "hello-buildroot"
help
Custom demo application.
EOF
cat > "$BR_DIR/package/$APP_NAME/$APP_NAME.mk" << EOF
################################################################################
# hello-buildroot
################################################################################
HELLO_BUILDROOT_VERSION = 1.0.0
HELLO_BUILDROOT_SITE = \$(TOPDIR)/../app_src/hello-buildroot
HELLO_BUILDROOT_SITE_METHOD = local
HELLO_BUILDROOT_LICENSE = MIT
define HELLO_BUILDROOT_BUILD_CMDS
\$(MAKE) CC=\$(TARGET_CC) -C \$(@D) all
endef
define HELLO_BUILDROOT_INSTALL_TARGET_CMDS
\$(INSTALL) -D -m 0755 \$(@D)/hello-buildroot \$(TARGET_DIR)/usr/bin/hello-buildroot
endef
\$(eval \$(generic-package))
EOF
echo "Step 3: Register package..."
if ! grep -q "hello-buildroot" "$BR_DIR/package/Config.in"; then
echo 'source "package/hello-buildroot/Config.in"' >> "$BR_DIR/package/Config.in"
fi
echo "Done! Now run:"
echo " cd $BR_DIR"
echo " make menuconfig # Enable hello-buildroot"
echo " make"
```
---
## 关键概念总结
| 步骤 | 文件/位置 | 作用 |
|------|-----------|------|
| 源码准备 | `app_src/hello-buildroot/` | 应用程序源代码 |
| 包描述 | `package/hello-buildroot/Config.in` | 在menuconfig中显示选项 |
| 构建规则 | `package/hello-buildroot/hello-buildroot.mk` | 定义如何编译和安装 |
| 包注册 | `package/Config.in` | 让Buildroot知道你的包 |
| 输出位置 | `output/target/` | 安装后的文件(构建时) |
| 最终镜像 | `output/images/rootfs.*` | 可烧录的根文件系统镜像 |
---
## 常见陷阱与解决方案
1. **问题**:编译时找不到头文件
**解决**:在 `.mk` 文件中添加依赖:
```makefile
HELLO_BUILDROOT_DEPENDENCIES = libfoo
```
2. **问题**:应用无法运行(缺少共享库)
**解决**:检查依赖并添加:
```makefile
HELLO_BUILDROOT_DEPENDENCIES = libcurl openssl
```
3. **问题**:修改源码后 `make` 没有重新编译
**解决**:删除构建目录:
```bash
rm -rf output/build/hello-buildroot-*
make hello-buildroot
```
4. **问题**:应用安装到了错误的位置
**解决**:检查 `INSTALL_TARGET_CMDS` 中的路径:
```makefile
$(INSTALL) -D -m 0755 $(@D)/hello-buildroot $(TARGET_DIR)/usr/bin/hello-buildroot
```
---
## 拓展:添加启动脚本
如果想让应用开机自动运行,可以添加启动脚本:
```makefile
# 在 hello-buildroot.mk 中添加
define HELLO_BUILDROOT_INSTALL_INIT_SYSV
$(INSTALL) -D -m 0755 package/hello-buildroot/S99hello \
$(TARGET_DIR)/etc/init.d/S99hello
endef
```
创建 `package/hello-buildroot/S99hello`:
```bash
#!/bin/sh
case "$1" in
start)
echo "Starting hello-buildroot..."
/usr/bin/hello-buildroot &
;;
stop)
killall hello-buildroot
;;
esac
exit 0
```
这样应用就会在系统启动时自动运行了。
---