pkg-config 默认不区分编译目标,必须设 pkg_config_sysroot_dir 和 pkg_config_path 并清空 pkg_config_libdir,否则会错误链接宿主机库;cmake 中需显式指定 pkg_config_executable 或改用 find_package(config)。

pkg-config 默认会查宿主机路径,不干预就必然链接错库。它根本不管你是交叉编译还是本地编译,只认 PKG_CONFIG_PATH 和系统默认路径(如 /usr/lib/pkgconfig)。你用 clang -target aarch64-unknown-linux-gnu 编译,它照样去读 x86_64 的 .pc 文件,结果头文件路径、库名、甚至 Libs: 里带的 -lfoo 都指向宿主机版本——链接时看似成功,运行时报 undefined symbol 或 GLIBC version mismatch 是常态。
为什么 pkg-config 会“自动认错家”
pkg-config 没有内置目标平台概念,它的行为完全由环境变量驱动:PKG_CONFIG_PATH 优先级最高,其次是 PKG_CONFIG_LIBDIR,最后 fallback 到编译时硬编码的系统路径(通常是 /usr/lib/pkgconfig 和 /usr/share/pkgconfig)。交叉编译时若没重置这两个变量,它就直接扫宿主机目录。
更隐蔽的问题是:有些项目构建脚本(比如 autotools 的 configure)会自动执行 pkg-config --exists foo 做依赖探测,即使你后面手动指定 --sysroot,探测阶段已经用错的 .pc 文件决定了启用哪些功能宏、是否定义某些符号——这比链接错误更难排查。
必须设 PKG_CONFIG_SYSROOT_DIR,且不能只设 PKG_CONFIG_PATH
只把目标平台的 .pc 文件拷到某个目录、再加进 PKG_CONFIG_PATH 是不够的。因为 .pc 文件里写的 prefix=/usr、libdir=${prefix}/lib 这类变量,仍会解析成宿主机的 /usr/lib,而不是你 sysroot 下的 /path/to/sysroot/usr/lib。
正确做法是同时设置:
-
PKG_CONFIG_SYSROOT_DIR=/path/to/sysroot—— 这会让 pkg-config 自动把所有.pc文件里的路径前缀替换成该值(例如${prefix}/include→/path/to/sysroot/usr/include) -
PKG_CONFIG_PATH=/path/to/sysroot/usr/lib/pkgconfig:/path/to/sysroot/usr/share/pkgconfig—— 指向目标平台的.pc目录,确保找到的是 ARM64 版本描述 - 清空
PKG_CONFIG_LIBDIR(或显式设为空),避免它 fallback 到系统默认路径
示例(Bash):
export PKG_CONFIG_SYSROOT_DIR="/opt/sysroot-aarch64" export PKG_CONFIG_PATH="/opt/sysroot-aarch64/usr/lib/pkgconfig:/opt/sysroot-aarch64/usr/share/pkgconfig" export PKG_CONFIG_LIBDIR=""
验证是否生效:pkg-config --variable=libdir zlib 应输出 /opt/sysroot-aarch64/usr/lib,而非 /usr/lib。
CMake 中 pkg-config 的坑:find_package() 不走 pkg-config 环境变量
如果你在 CMakeLists.txt 里写 find_package(OpenSSL) 或 pkg_check_modules(ZLIB REQUIRED zlib),CMake 默认不会读取你设的 PKG_CONFIG_* 变量——它有自己的 pkg-config 查找逻辑,而且优先使用缓存值。
必须显式告诉 CMake 使用交叉环境:
- 对
pkg_check_modules,加IMPORTED_TARGET并确保PKG_CONFIG_EXECUTABLE指向目标平台专用的 pkg-config(如有);但更稳妥的是直接禁用自动查找,改用find_path/find_library手动定位 sysroot 内路径 - 全局强制 CMake 尊重环境变量:
cmake -DCMAKE_PREFIX_PATH=/opt/sysroot-aarch64/usr -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config ... - 最干净的做法:不用
pkg_check_modules,改用find_package(... CONFIG)并指定PATHS /opt/sysroot-aarch64/usr/lib/cmake/...,绕过 pkg-config 一层
真正麻烦的不是设对变量,而是很多开源项目(尤其是老项目)的构建脚本根本不检查 PKG_CONFIG_SYSROOT_DIR,或者硬编码了 pkg-config 路径。这时候得要么打 patch,要么用 wrapper 脚本劫持 pkg-config 调用——但凡看到 configure 输出里有 checking for zlib... yes 却没报错,就得立刻 pkg-config --libs zlib 看它实际返回了什么路径,别等链接完才发现用了宿主机的 libz.so.1。











