19 使用预构建库

19 简介

有些库供应商不发布源码,只提供预构建二进制文件。构建共享库时应附加版本 信息,背景知识见 这篇文章, 但有时供应商没有这样做。

带版本的库必须满足两个条件:

  1. 文件名附加版本号,例如 libfoo.so.1.2.3。

  2. 库的 ELF 标签 SONAME 设为库的主版本,例如 libfoo.so.1。 可运行 readelf -d filename | grep SONAME 检查。

本节介绍如何处理带版本和不带版本的预构建库。

19 带版本的库

本例使用 FT4222H USB I/O 芯片的预构建库。供应商为多个目标架构变体构建了 库,并按以下结构打包:

├── build-arm-hisiv300
│   └── libft4222.so.1.4.4.44
├── build-arm-v5-sf
│   └── libft4222.so.1.4.4.44
├── build-arm-v6-hf
│   └── libft4222.so.1.4.4.44
├── build-arm-v7-hf
│   └── libft4222.so.1.4.4.44
├── build-arm-v8
│   └── libft4222.so.1.4.4.44
├── build-i386
│   └── libft4222.so.1.4.4.44
├── build-i486
│   └── libft4222.so.1.4.4.44
├── build-mips-eglibc-hf
│   └── libft4222.so.1.4.4.44
├── build-pentium
│   └── libft4222.so.1.4.4.44
├── build-x86_64
│   └── libft4222.so.1.4.4.44
├── examples
│   ├── get-version.c
│   ├── i2cm.c
│   ├── spim.c
│   └── spis.c
├── ftd2xx.h
├── install4222.sh
├── libft4222.h
├── ReadMe.txt
└── WinTypes.h

编写配方以在系统中使用此类库时:

  • 供应商可能采用专有许可证,因此应在配方中设置 LICENSE_FLAGS。

  • 供应商提供包含库的归档包,应相应设置 SRC_URI。

  • 设置 COMPATIBLE_HOST,避免在不受支持的架构上使用配方。以下示例 只支持 x86 架构的 32 位和 64 位变体。

  • 供应商提供的是带版本的库,因此可以使用 utils 中的 oe_soinstall 安装共享库并创建符号链接。若库不带版本,应遵循下一节。

  • 供应商使用的 LDFLAGS 很可能与当前 Yocto Project 构建不同,应将 ldflags 加入 INSANE_SKIP,禁用对应检查。

  • 供应商通常提供不含调试符号的发行构建。应通过下面的 INHIBIT_ 标志, 禁止打包任务剥离符号并将其放入独立调试软件包,避免出现错误。

完整配方如下:

SUMMARY = "FTDI FT4222H Library"
SECTION = "libs"
LICENSE_FLAGS = "ftdi"
LICENSE = "CLOSED"

COMPATIBLE_HOST = "(i.86|x86_64).*-linux"

# Sources available in a .tgz file in .zip archive
# at https://ftdichip.com/wp-content/uploads/2021/01/libft4222-linux-1.4.4.44.zip
# Found on https://ftdichip.com/software-examples/ft4222h-software-examples/
# Since dealing with this particular type of archive is out of topic here,
# we use a local link.
SRC_URI = "file://libft4222-linux-${PV}.tgz"

S = "${UNPACKDIR}"

ARCH_DIR:x86-64 = "build-x86_64"
ARCH_DIR:i586 = "build-i386"
ARCH_DIR:i686 = "build-i386"

INSANE_SKIP:${PN} = "ldflags"
INHIBIT_PACKAGE_STRIP = "1"
INHIBIT_SYSROOT_STRIP = "1"
INHIBIT_PACKAGE_DEBUG_SPLIT = "1"

do_install () {
        install -m 0755 -d ${D}${libdir}
        oe_soinstall ${S}/${ARCH_DIR}/libft4222.so.${PV} ${D}${libdir}
        install -d ${D}${includedir}
        install -m 0755 ${S}/*.h ${D}${includedir}
}

如果预编译二进制文件不是静态链接,且依赖其他库,将这些库加入 DEPENDS 后,构建系统就可以检查链接关系,并自动添加适当的 RDEPENDS。

19 不带版本的库

19 背景知识

Linux 系统中的库通常带有版本,便于同时安装同一库的多个版本,从而简化 升级并支持旧软件。例如,实际库为 libfoo.so.1.2,符号链接 libfoo.so.1 和 libfoo.so 均指向它。将二进制程序链接到库时,通常提供不带版本的 名称,例如向链接器传递 -lfoo;但链接器会沿符号链接,实际链接到带版本 的文件名。不带版本的符号链接仅在开发时使用,因此它与头文件一起放入开发 软件包 ${PN}-dev,实际库和带版本的符号链接则放入 ${PN}。带版本 的库远比不带版本的库常见,所以默认打包规则假定库带版本。

19 Yocto 库打包概述

因此,打包不带版本的库需要调整配方。默认情况下,libfoo.so 会进入 ${PN}-dev,触发“非符号链接库位于 -dev 软件包”的 QA 警告;同一 配方中的二进制程序链接到 ${PN}-dev 中的库,还会触发其他 QA 警告。 解决方法是将不带版本的库打包到其应在的 ${PN}。bitbake.conf 中 默认 FILES 变量的简化内容如下:

SOLIBS = ".so.*"
SOLIBSDEV = ".so"
FILES:${PN} = "... ${libdir}/lib*${SOLIBS} ... ${base_libdir}/*${SOLIBS} ..."
FILES_SOLIBSDEV ?= "${base_libdir}/lib*${SOLIBSDEV} ${libdir}/lib*${SOLIBSDEV}"
FILES:${PN}-dev = "... ${FILES_SOLIBSDEV} ..."

SOLIBS 定义匹配实际共享库的模式,SOLIBSDEV 匹配开发形式, 即不带版本的符号链接。FILES:${PN} 和 FILES:${PN}-dev 使用这两个 变量,将实际库放入 ${PN},将不带版本的符号链接放入 ${PN}-dev。 要打包不带版本的库,应在配方中如下修改变量:

SOLIBS = ".so"
FILES_SOLIBSDEV = ""

这样会将 .so 文件视为实际库,并清空 FILES_SOLIBSDEV,防止 任何库进入 ${PN}-dev。这是必要的,因为除非修改 PACKAGES, ${PN}-dev 总会先于 ${PN} 收集文件;它不能抢先收集本应放入 ${PN} 的文件。

可加载模块本质上是不带版本的库,在运行时通过 dlopen() 加载,而不是 构建时链接。通常应将它们安装到私有目录。如果安装到 ${libdir}, 则可以按不带版本的库处理。

19 示例

以下示例安装名为 libfoo.so 的不带版本 x86-64 预构建库。 COMPATIBLE_HOST 将配方限制在 x86-64 架构;INSANE_SKIP、 INHIBIT_PACKAGE_STRIP 和 INHIBIT_SYSROOT_STRIP 与前面的 带版本库示例设置相同。关键是按上述说明设置 SOLIBS 和 FILES_SOLIBSDEV:

SUMMARY = "libfoo sample recipe"
SECTION = "libs"
LICENSE = "CLOSED"

SRC_URI = "file://libfoo.so"

COMPATIBLE_HOST = "x86_64.*-linux"

INSANE_SKIP:${PN} = "ldflags"
INHIBIT_PACKAGE_STRIP = "1"
INHIBIT_SYSROOT_STRIP = "1"
SOLIBS = ".so"
FILES_SOLIBSDEV = ""

do_install () {
        install -d ${D}${libdir}
        install -m 0755 ${UNPACKDIR}/libfoo.so ${D}${libdir}
}