增加应用程序和第三方库

概述

本文档指导用户在 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!

注意事项

  1. 层优先级:确保自定义层的配置正确,避免与其他层冲突
  2. 许可证声明:务必正确设置 LICENSE 和 LIC_FILES_CHKSUM
  3. 文件权限:注意可执行文件和库文件的权限设置
  4. 符号链接:动态库的符号链接必须正确设置
  5. 依赖关系:如果库依赖其他包,需在配方中声明 DEPENDS
  6. 版本管理:建议使用版本号管理库文件

常见问题

Q1: BitBake 编译失败?

检查项

  • 配方语法是否正确
  • 源文件路径是否正确
  • LICENSE 文件是否存在

Q2: IPK 安装失败?

解决方法

  • 确保文件系统已挂载为读写模式
  • 检查磁盘空间是否充足
  • 查看 opkg 错误日志

Q3: 动态库找不到?

解决方法

  • 检查 LD_LIBRARY_PATH 环境变量
  • 确认符号链接是否正确
  • 运行 ldconfig 更新库缓存

相关文档

总结

通过本文档的指导,您可以在 Yocto 环境中:

  • 创建自定义应用程序和库
  • 生成 IPK 安装包进行单独部署
  • 将组件集成到系统镜像中
  • 验证和测试自定义组件

掌握这些技能后,您可以根据实际需求扩展和定制系统功能。