增加应用程序和第三方库
概述
本文档指导用户在 Yocto 构建系统中添加自定义应用程序和第三方库(以 helloworld 为例)。通过本文档的步骤,您可以:
- 将自定义代码集成到 Yocto 项目中
- 生成 IPK 安装包
- 编译和使用第三方库文件
- 将组件集成到系统镜像中
前置准备
环境要求
- 已安装必要的开发工具包
- 已克隆 Yocto 项目代码
- 熟悉基本的 C 语言编程
- 了解 Makefile 和 BitBake 基础
项目目录结构
创建自定义 meta 层,推荐使用以下目录结构:
meta-custom/
├── conf/
│ └── layer.conf # 层配置文件
├── recipes-apps/ # 应用程序配方目录
│ └── helloworld/
│ ├── files/
│ │ ├── helloworld.c # 应用源码
│ │ ├── Makefile # 编译脚本
│ │ └── helloworld.service # systemd 服务文件
│ └── helloworld_1.0.bb # BitBake 配方
└── recipes-libs/ # 库配方目录
└── lib-helloworld/
├── files/
│ ├── lib_helloworld.c # 库源码
│ ├── lib_helloworld.h # 库头文件
│ └── Makefile # 编译脚本
└── lib-helloworld_1.0.bb # BitBake 配方
添加自定义应用程序
1. 创建层目录结构
创建目录
mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-apps/helloworld/files
mkdir -p ${YOCTO_DIR}/layers/meta-custom/conf
配置自定义层
在 layers/meta-custom/conf/layer.conf 文件中添加:
BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb"
LAYERDEPENDS_meta-custom = "core"
LAYERSERIES_COMPAT_meta-custom = "kirkstone"
注册层到构建系统
编辑 layers/meta-qcom-distro/conf/bblayers.conf,添加自定义层路径:
BBLAYERS = " \
${WORKSPACE}/layers/meta-custom \
"
添加到镜像配方
编辑 layers/meta-quectel/recipes-products/images/qcom-multimedia-image.bbappend,添加:
# 添加应用程序
IMAGE_INSTALL:append = " helloworld"
# 添加库文件(如果需要)
IMAGE_INSTALL:append = " \
lib-helloworld \
lib-helloworld-dev \
lib-helloworld-staticdev \
"
2. 创建应用程序源码
编写主程序
在 meta-custom/recipes-apps/helloworld/files/ 目录下创建 helloworld.c:
// helloworld.c
#include <stdio.h>
int main() {
printf("Hello, world!\n");
return 0;
}
编写 Makefile
在 meta-custom/recipes-apps/helloworld/files/ 目录下创建 Makefile:
# 使用 Yocto 提供的编译标志
all: helloworld
helloworld: helloworld.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
clean:
rm -f helloworld
.PHONY: all clean
创建 Systemd 服务文件
在 meta-custom/recipes-apps/helloworld/files/ 目录下创建 helloworld.service:
[Unit]
Description=Hello World Service
[Service]
Type=simple
ExecStart=/usr/bin/helloworld
[Install]
WantedBy=multi-user.target
3. 编写 BitBake 配方
在 meta-custom/recipes-apps/helloworld/ 目录下创建 helloworld_1.0.bb:
SUMMARY = "Hello World application"
DESCRIPTION = "A simple Hello World application."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
# 源代码文件列表
SRC_URI = "file://helloworld.c \
file://Makefile \
file://helloworld.service"
# 设置工作目录
S = "${WORKDIR}"
# 继承 systemd 类
inherit systemd
# 定义 systemd 服务文件名
SYSTEMD_SERVICE:${PN} = "helloworld.service"
# 配置阶段(跳过)
do_configure() {
bbnote "skip do_configure"
}
# 编译阶段
do_compile() {
# 先清理
oe_runmake -f ${S}/Makefile clean
# 执行编译
oe_runmake -f ${S}/Makefile
}
# 安装阶段
do_install() {
# 创建可执行文件安装目录
install -d ${D}${bindir}
# 安装可执行文件
install -m 0755 ${S}/helloworld ${D}${bindir}
# 创建 systemd 服务目录
install -d ${D}${systemd_system_unitdir}
# 安装服务文件
install -m 0644 ${S}/helloworld.service ${D}${systemd_system_unitdir}
}
4. 构建和安装应用程序
配置构建环境
进入代码工作目录,执行:
source quectel_build/compile/build.sh
方式一:单独编译并安装 IPK 包
步骤 1:编译应用程序
bitbake helloworld
步骤 2:查找生成的 IPK 包
find ~/build-qcom-wayland/tmp-glibc/deploy -name "helloworld*.ipk"
输出示例:
/home/quectel/build-qcom-wayland/tmp-glibc/deploy/ipk/armv8-2a/helloworld_1.0-r0_armv8-2a.ipk
步骤 3:推送到设备
adb push helloworld_1.0-r0_armv8-2a.ipk /mnt
步骤 4:安装 IPK 包
在设备上执行:
# 挂载文件系统为读写模式
mount -o remount,rw /usr
# 安装 IPK 包
opkg install /mnt/helloworld_1.0-r0_armv8-2a.ipk
步骤 5:验证安装
/usr/bin/helloworld
预期输出:
Hello, world!
方式二:整编系统镜像
步骤 1:配置构建参数
# 用户可自定义输入参数,从提示的有效选项中选择
buildconfig QSM565DWF SG565DWFPARL1A01_BL01BP01K0M01_QDP_LP6.6.0XX.01.00X_V0X STD
步骤 2:编译整个系统
buildall
步骤 3:打包镜像
buildpackage
步骤 4:烧录镜像
生成的镜像位于 quectel_build/SG565DWFPARL1A01_BL01BP01K0M01_QDP_LP6.6.0XX.01.00X_V0X 路径下。
参考以下文档进行烧录:
步骤 5:验证安装
烧录完成后,在设备上执行:
/usr/bin/helloworld
预期输出:
Hello, world!
添加第三方库
1. 创建库文件
创建目录
mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-libs/lib-helloworld/files
创建库头文件
在 meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 lib_helloworld.h:
// lib_helloworld.h
#ifndef LIB_HELLO_H
#define LIB_HELLO_H
void hello_print(const char* name);
#endif
创建库源码
在 meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 lib_helloworld.c:
// lib_helloworld.c
#include <stdio.h>
#include "lib_helloworld.h"
void hello_print(const char* name) {
printf("[LIB] Hello, %s!\n", name);
}
编写 Makefile
在 meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 Makefile:
LIB_VERSION = 1.0
all:
${CC} ${CFLAGS} -fPIC -c lib_helloworld.c -o lib_helloworld.o
${CC} ${CFLAGS} -fPIC -shared lib_helloworld.o -o libhello.so.${LIB_VERSION} \
-Wl,-soname,libhello.so.1 ${LDFLAGS}
ar rcs libhello.a lib_helloworld.o
ln -sf libhello.so.${LIB_VERSION} libhello.so
ln -sf libhello.so.${LIB_VERSION} libhello.so.1
clean:
rm -f libhello.so* lib_helloworld.o libhello.a
2. 编写 BitBake 配方
在 meta-custom/recipes-libs/lib-helloworld/ 目录下创建 lib-helloworld_1.0.bb:
SUMMARY = "Hello World Library"
SECTION = "libs"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://lib_helloworld.c \
file://lib_helloworld.h \
file://Makefile"
S = "${WORKDIR}"
EXTRA_OEMAKE = "'CC=${CC}' 'CFLAGS=${CFLAGS} -fPIC' 'LDFLAGS=${LDFLAGS}'"
do_install() {
# 创建目标目录
install -d ${D}${libdir}
install -d ${D}${includedir}
# 安装共享库及符号链接
install -m 0755 ${S}/libhello.so.${PV} ${D}${libdir}/
ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so
ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so.1
# 安装静态库
install -m 0644 ${S}/libhello.a ${D}${libdir}/
# 安装头文件
install -m 0644 ${S}/lib_helloworld.h ${D}${includedir}/
}
# 包文件分配
FILES:${PN} = " \
${libdir}/libhello.so.1 \
${libdir}/libhello.so.${PV} \
"
FILES:${PN}-dev = " \
${libdir}/libhello.so \
${includedir}/lib_helloworld.h \
"
FILES:${PN}-staticdev = " \
${libdir}/libhello.a \
"
3. 构建和使用第三方库
配置构建环境
source quectel_build/compile/build.sh
方式一:单独编译并安装
步骤 1:编译库
bitbake lib-helloworld
步骤 2:查找生成的库文件
find ./* -name "libhello*"
步骤 3:推送到设备
adb push libhello.so libhello.so.1 libhello.so.1.0 libhello.a /usr/lib
步骤 4:修复权限和符号链接
在设备上执行:
# 设置执行权限
chmod 755 /usr/lib/libhello.so*
chmod 644 /usr/lib/libhello.a
# 重建符号链接
cd /usr/lib
ln -sf libhello.so.1.0 libhello.so.1
ln -sf libhello.so.1 libhello.so
步骤 5:编写测试程序
创建静态库测试程序 static_test_helloworld.c:
#include <stdio.h>
void hello_print(const char*);
int main() {
printf("static lib test\n");
hello_print("world");
return 0;
}
创建动态库测试程序 dynamic_test_helloworld.c:
#include <stdio.h>
void hello_print(const char*);
int main() {
printf("dynamic lib test\n");
hello_print("world");
return 0;
}
步骤 6:编译并运行测试程序
静态库测试:
gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello
预期输出:
static lib test
[LIB] Hello, world!
动态库测试:
gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello
预期输出:
dynamic lib test
[LIB] Hello, world!
方式二:整编系统镜像
编译和烧录
参考前面"添加自定义应用程序"章节中"方式二:整编系统镜像"的步骤进行编译和烧录。
验证库文件
烧录完成后,在设备上检查库文件:
ls -l /usr/lib/libhello*
预期输出:
-rw-r--r--. 2 root root 5002 Jan 1 1970 /usr/lib/libhello.a
lrwxrwxrwx. 1 root root 15 Jul 25 06:26 /usr/lib/libhello.so -> libhello.so.1.0
lrwxrwxrwx. 1 root root 15 Jul 25 06:26 /usr/lib/libhello.so.1 -> libhello.so.1.0
-rwxr-xr-x. 2 root root 5968 Jan 1 1970 /usr/lib/libhello.so.1.0
运行测试程序
静态库测试:
gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello
预期输出:
static lib test
[LIB] Hello, world!
动态库测试:
gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello
预期输出:
dynamic lib test
[LIB] Hello, world!
注意事项
- 层优先级:确保自定义层的配置正确,避免与其他层冲突
- 许可证声明:务必正确设置 LICENSE 和 LIC_FILES_CHKSUM
- 文件权限:注意可执行文件和库文件的权限设置
- 符号链接:动态库的符号链接必须正确设置
- 依赖关系:如果库依赖其他包,需在配方中声明 DEPENDS
- 版本管理:建议使用版本号管理库文件
常见问题
Q1: BitBake 编译失败?
检查项:
- 配方语法是否正确
- 源文件路径是否正确
- LICENSE 文件是否存在
Q2: IPK 安装失败?
解决方法:
- 确保文件系统已挂载为读写模式
- 检查磁盘空间是否充足
- 查看 opkg 错误日志
Q3: 动态库找不到?
解决方法:
- 检查 LD_LIBRARY_PATH 环境变量
- 确认符号链接是否正确
- 运行
ldconfig更新库缓存
相关文档
总结
通过本文档的指导,您可以在 Yocto 环境中:
- 创建自定义应用程序和库
- 生成 IPK 安装包进行单独部署
- 将组件集成到系统镜像中
- 验证和测试自定义组件
掌握这些技能后,您可以根据实际需求扩展和定制系统功能。