3、依赖库准备(上):下载并编译zlib、libffi、glib的ARM版本

好,咱们继续往下走。上一章我们把交叉编译工具链和sysroot都准备好了,现在手里有家伙了,该干活了。

这一章,我们要搞定三个最基础的依赖库:zliblibffiglib。为什么先搞它们?因为GStreamer的底层离不开glib,而glib又依赖zlib和libffi。说白了,这就是个套娃结构,得从最里面那个娃开始拆。

我个人习惯,编译这种基础库,一定要先搞清楚它们的依赖关系。不然你编译到一半,突然报个错说找不到某个头文件,那才叫一个头大。

3.1 准备工作:环境变量与目录结构

在开始之前,先把环境变量确认一下。我假设你跟我一样,把交叉编译工具链装在了 /opt/arm-gcc 目录下,sysroot在 /opt/arm-sysroot

每次打开新终端,我建议你先跑一遍这个:

export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
export AR=arm-linux-gnueabihf-ar
export RANLIB=arm-linux-gnueabihf-ranlib
export SYSROOT=/opt/arm-sysroot
export PREFIX=$SYSROOT/usr
export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig
export PKG_CONFIG_SYSROOT_DIR=$SYSROOT
export CFLAGS="--sysroot=$SYSROOT -I$PREFIX/include"
export LDFLAGS="--sysroot=$SYSROOT -L$PREFIX/lib"

嗯,这里要注意:PREFIX 一定要指向sysroot下的 usr 目录。为什么?因为后面编译出来的 .pc 文件(pkg-config用的)里会记录这个路径,如果你随便放个地方,后面找库的时候会乱套。我在项目中就吃过这个亏,编译完glib后怎么都找不到,最后发现是路径写死了。

3.2 编译zlib:最基础的压缩库

zlib是个轻量级的压缩库,GStreamer里很多插件都用它来处理数据流。编译它相对简单,但有几个坑要注意。

先下载源码:

wget https://zlib.net/zlib-1.2.13.tar.gz
tar -xzf zlib-1.2.13.tar.gz
cd zlib-1.2.13

zlib的构建系统比较老,用的是自己写的configure脚本,不是autotools。所以配置方式有点不一样:

CHOST=arm-linux-gnueabihf \
./configure --prefix=$PREFIX --static

这里我加了 --static 参数。为什么?因为嵌入式平台上,静态链接能减少运行时依赖,避免目标板上缺库的尴尬。你想想看,你的程序在开发板上跑起来,结果报错说 libz.so.1 not found,那多闹心。

配置完直接编译安装:

make -j4
make install

验证一下:

arm-linux-gnueabihf-objdump -p $PREFIX/lib/libz.a | grep -i "architecture"

如果输出显示 ARM,那就对了。

小提示: 如果你需要动态库版本,去掉 --static 即可。但记得把生成的 libz.so.1 拷贝到目标板的 /lib/usr/lib 下。

3.3 编译libffi:外部函数接口库

libffi这个库,说白了就是让程序能在运行时动态调用C函数。glib的GObject类型系统底层依赖它。我记得第一次接触libffi时,觉得这东西很神秘,后来发现其实就是个跳板。

下载源码:

wget https://github.com/libffi/libffi/releases/download/v3.4.4/libffi-3.4.4.tar.gz
tar -xzf libffi-3.4.4.tar.gz
cd libffi-3.4.4

libffi用的是autotools构建系统,配置命令如下:

./configure \
  --host=arm-linux-gnueabihf \
  --prefix=$PREFIX \
  --with-sysroot=$SYSROOT \
  --enable-static \
  --disable-shared

这里有个关键参数 --with-sysroot,它告诉configure脚本去哪里找依赖的头文件和库。如果不加这个,它可能会去宿主机的 /usr/include 里找,那编译出来的东西就串味了。

编译安装:

make -j4
make install
注意: 我曾经在编译libffi时遇到过 undefined reference to `ffi_prep_closure' 的错误。这是因为某些架构需要开启 --enable-closures 选项。如果你遇到类似问题,可以加上 --enable-closures 重新配置。

安装完后,检查一下生成的 .pc 文件:

cat $PREFIX/lib/pkgconfig/libffi.pc

确保 prefixexec_prefix 指向的是你的sysroot路径,而不是宿主机的路径。

3.4 编译glib:GStreamer的基石

glib是GStreamer最核心的依赖,没有之一。它提供了事件循环、线程管理、内存分配、类型系统等基础设施。GStreamer的pipeline、buffer、element这些概念,底层都是基于glib实现的。

下载源码:

wget https://download.gnome.org/sources/glib/2.78/glib-2.78.0.tar.xz
tar -xf glib-2.78.0.tar.xz
cd glib-2.78.0

glib从2.60版本开始,构建系统从autotools切换到了meson。所以配置方式变了:

meson setup build \
  --cross-file /path/to/arm-cross-file.txt \
  --prefix $PREFIX \
  --libdir lib \
  --default-library static \
  -Dtests=false \
  -Dselinux=disabled \
  -Dlibmount=disabled

这里需要准备一个交叉编译配置文件 arm-cross-file.txt,内容如下:

[binaries]
c = 'arm-linux-gnueabihf-gcc'
cpp = 'arm-linux-gnueabihf-g++'
ar = 'arm-linux-gnueabihf-ar'
strip = 'arm-linux-gnueabihf-strip'
pkgconfig = 'arm-linux-gnueabihf-pkg-config'

[host_machine]
system = 'linux'
cpu_family = 'arm'
cpu = 'armv7l'
endian = 'little'

[properties]
sys_root = '/opt/arm-sysroot'
pkg_config_libdir = '/opt/arm-sysroot/usr/lib/pkgconfig'

这个文件告诉meson:你的目标机器是ARM架构,编译器用哪个,sysroot在哪。我个人习惯把这个文件放在源码目录下,方便以后复用。

配置完成后,编译安装:

meson compile -C build
meson install -C build
重点: glib编译过程中可能会报错说找不到 libffizlib。这是因为meson没有自动找到我们之前编译好的库。解决办法是设置 PKG_CONFIG_PATH 环境变量,指向我们安装库的 pkgconfig 目录。如果你按照我前面的步骤做了,这一步应该不会出问题。

验证glib是否编译成功:

arm-linux-gnueabihf-gcc --sysroot=$SYSROOT -o test_glib test_glib.c \
  $(pkg-config --cflags --libs glib-2.0)

其中 test_glib.c 可以写一个简单的glib程序:

#include <glib.h>
int main() {
    g_print("Hello from GLib %d.%d.%d\n",
            GLIB_MAJOR_VERSION,
            GLIB_MINOR_VERSION,
            GLIB_MICRO_VERSION);
    return 0;
}

如果能编译通过,说明glib已经成功安装到sysroot里了。

3.5 常见问题与避坑指南

编译这三个库时,我遇到过不少坑,这里列几个典型的:

问题 原因 解决方案
configure: error: C compiler cannot create executables 交叉编译器路径不对或sysroot配置错误 检查 CCCFLAGS 环境变量,确保 --sysroot 指向正确的目录
undefined reference to `ffi_type_uint32' libffi未正确安装或链接顺序不对 重新编译libffi,确保 make install 成功,并在链接时加上 -lffi
glib: ERROR: Problem encountered: glib requires libffi >= 3.0.0 pkg-config找不到libffi的.pc文件 设置 PKG_CONFIG_PATH 指向libffi的pkgconfig目录
meson: ERROR: Unknown compiler(s): [['cc'], ['gcc']] meson没有找到交叉编译器 检查 arm-cross-file.txt 中的编译器路径是否正确

嗯,这里我要特别说一下。我曾经在编译glib时,卡在 libffi 的依赖上整整半天。后来发现是 PKG_CONFIG_PATH 没设置对,meson一直去宿主机的目录找libffi,结果找到的是x86版本的,链接时当然报错。所以,环境变量一定要检查三遍

3.6 本章小结

这一章我们完成了三个基础库的交叉编译:

  • zlib:最基础的压缩库,编译最简单,注意用 CHOST 指定目标架构
  • libffi:外部函数接口,glib的底层依赖,注意 --with-sysroot 参数
  • glib:GStreamer的基石,用meson构建,需要准备交叉编译配置文件

这三个库都安装到了sysroot的 /usr 目录下。下一章,我们会继续编译GStreamer的其他依赖库,比如 libxml2orc 等。到时候你会发现,有了这一章的基础,后面的编译会顺畅很多。

好,今天就到这里。如果你在编译过程中遇到任何问题,欢迎随时交流。记住,交叉编译就是个细心活,耐心点,总能搞定。