在 OpenEuler 上编译软件,最磨人的往往不是代码本身,而是卡在各种依赖检查上。最近我在编译一个需要压缩库支持的项目时就撞上了这个经典问题:configure脚本一路跑着,到了checking for liblz4... no直接停住,紧接着抛出一句configure: error: Package requirements (liblz4) were not met。当时第一反应是“liblz4 没装?那就装呗”,结果装上之后再跑居然还是一模一样的报错,这就让人有点上火了。
如果你也遇到过类似的Package requirements报错,大概率是踩进了同一个坑:系统里确实有liblz4这个库,但configure脚本需要的并不是库本体,而是用于描述库的开发文件——也就是.pc文件和相关头文件。在 OpenEuler(尤其是最小化安装环境)上,这个问题特别典型,因为默认源里很多编译期依赖都不会自动装。这篇文章就从我这回的实际排查过程讲起,把这个报错从根因到解决方案完整拆一遍,包括:liblz4和liblz4-devel的区别、pkg-config的查找机制、OpenEuler 特有的软件包装包习惯,以及在离线或 ARM 环境下怎么处理。不管你是刚接触 OpenEuler 的初学者,还是被这类依赖问题绊住过的老手,这篇都能给你一个可以直接照做的完整思路。
1. 拆解checking for liblz4... no:configure 到底在检查什么
1.1 configure 脚本与pkg-config的工作关系
首先要搞清楚一件事:configure报错说“Package requirements (liblz4) were not met”,反馈路径其实是这样的——绝大多数现代开源项目在检测库依赖时,不是直接去/usr/lib里翻有没有.so文件,而是调用一个叫pkg-config的小工具,让它去查对应库的.pc元信息文件。.pc文件里记录了库的版本号、头文件路径、链接参数、依赖关系等编译时必须的信息。
类比一下:如果把编译软件比作做一道菜,库本体(.so文件)相当于“食材”,而.pc文件和头文件(.h)就是“菜谱”。configure要的是菜谱加上食材,只给食材它还是会跟你急。checking for liblz4... no这一行垃圾信息般的“no”,实际含义就是pkg-config在它找过的所有路径里,都没有发现叫liblz4.pc的文件。
pkg-config默认搜索路径一般是/usr/lib/pkgconfig、/usr/share/pkgconfig、/usr/lib64/pkgconfig,以及环境变量PKG_CONFIG_PATH里指定的目录。在 OpenEuler 上,64 位系统的库文件主要在/usr/lib64,对应.pc文件通常也放在/usr/lib64/pkgconfig。如果你用dnf装的是不带-devel后缀的运行时包,那么.pc文件、头文件、静态库统统都不会装进去——这就是报错一直存在的核心原因。
1.2 报错日志里值得细看的两行信息
很多人一看到configure: error就慌了,其实往上看两行会有更多线索。以我这次遇到的报错为例,完整输出大概是:
checking for liblz4... no configure: error: Package requirements (liblz4) were not met: No package 'liblz4' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix.这两段提示分别说了两层意思。第一层是No package 'liblz4' found:pkg-config没找到.pc文件。第二层“Consider adjusting the PKG_CONFIG_PATH”也很关键——这是告诉我们,如果库是通过自定义路径编译安装的,那就需要手动把它的.pc文件路径加进环境变量,让configure能找到。当你的依赖不是用系统包管理器装的,而是从源码编译安装到了/usr/local或自定义目录,这个提示就是破解谜题的钥匙。
另外还有一种隐蔽情况:pkg-config确实找到了某个旧版本的liblz4.pc,但版本号不满足项目的最低要求(比如项目要 >=1.8.0,系统里只有 1.7.5),这时报错信息里会出现liblz4 >= 1.8.0的字样,而不是简单的“No package”。排查时先看清楚是“找不到”还是“版本不满足”,方向完全不同。
1.3 为什么 OpenEuler 上这个报错特别常见
OpenEuler 在服务器场景用得很多,不少人拿到手的是“最小化安装”或容器镜像,系统只带了运行必需的基本组件。很多编译工具链(如gcc、make、pkgconf)和开发库(-devel包)默认都不在安装列表里。更值得注意的是,OpenEuler 的软件源结构里有BaseOS、EPOL、optional等多个仓库,某些包在默认启用的仓库里没有,需要额外启用仓库才能搜到。这导致了不少人dnf search liblz4或者dnf install liblz4-devel的时候直接提示“没有可用软件包”,一头雾水的同时更确认“不是没装的问题”。
我在实际环境里做过验证:一个刚装好的 OpenEuler 22.03 LTS 最小化系统,rpm -qa | grep lz4结果可能是空的,也可能只有lz4-1.8.3-3这种不带devel的名字。无论哪种情况,直接pkg-config --libs liblz4都会返回非零状态码,这就是configure判定“no”的直接依据。
2. 别急着卸载重装:先看清 OpenEuler 的包装包结构与依赖链
2.1liblz4与liblz4-devel:只一字之差,却是完整流程的分水岭
在yum/dnf系发行版(包括 OpenEuler)里,一个库通常拆成两个包:运行时包和开发包。运行时包只有.so文件(比如liblz4.so.1),作用是让已经编译好的程序在运行阶段能加载这个动态库;开发包则包含头文件(.h)、静态库(.a 或 .so 软链接)和pkg-config 的 .pc 文件,是为编译阶段准备的。
对configure脚本来说,它要的就是开发包。只装运行时包,就好比给了你一辆车但没给钥匙——车在,但用不了。所以最直接的解决方案是:
dnf install liblz4-devel装完后可以立刻验证:
pkg-config --modversion liblz4 pkg-config --cflags --libs liblz4第一条命令会输出版本号(比如1.9.2),第二条会输出-llz4,看到这些就说明configure能正常通过了。
2.2 OpenEuler 的包命名差异与仓库启用问题
这里我必须专门提醒一下:OpenEuler 的软件包名和 CentOS/RHEL 并不总是完全一致,而且不同小版本(比如 22.03 LTS、22.03 LTS SP1/SP2/SP3、20.03 LTS)之间,仓库内容和默认启用情况有差异。在某些版本上,liblz4-devel在BaseOS仓库里就有;而在另一些版本上,你直接搜dnf list liblz4*可能只能看到liblz4,没有liblz4-devel,这时需要确认是否启用了全部仓库:
dnf repolist dnf list liblz4*如果dnf list里确实没有包名,先把所有仓库启用再搜一遍:
dnf install dnf-plugins-core dnf config-manager --set-enabled everything dnf makecache dnf search lz4在 OpenEuler 上,everything仓库包含很多基础开发包,不少devel包都放在这里,默认却不启用。我自己在 22.03 SP2 上就碰到过:BaseOS里查无此包,启用everything之后liblz4-devel就出来了。这个问题如果不知道,很容易误判成“OpenEuler 居然连 liblz4 都没有”。
2.3 编译链的中间层:某些情况下你还需要pkgconf
讲完包名,还有一个小坑差点坑了我:pkg-config命令本身也可能没装。OpenEuler 最小化系统里,pkgconf或pkg-config不一定在系统里,而configure脚本恰恰依赖它来检查所有依赖库。如果你的设备上连pkg-config --version都会报“command not found”,那也谈不上后续排查了。
顺手检查一下:
which pkg-config pkg-config --version如果不在,就先:
dnf install pkgconf注意 OpenEuler 的包名是pkgconf,它同时提供/usr/bin/pkg-config这个命令。我在实际项目里还见过一种情况:pkgconf装了,但configure还是找不到.pc文件——一查才发现系统里同时存在旧版pkg-config和pkgconf的命令路径冲突,configure实际调用的那个不是新装的。这种低级错误虽然少见,但真遇上了会浪费很多时间。
3. 完整解决过程记录:从报错到编译通过的三步走
3.1 第一步:快速诊断当前系统的 liblz4 状态
建议严格按照下面这个顺序排查,不要跳步:
# 1. 看有没有装过和 lz4 相关的包 rpm -qa | grep -i lz4 # 2. 看 pkg-config 能否找到 liblz4 pkg-config --modversion liblz4 # 3. 看 .pc 文件存在与否,以及它可能的位置 find /usr/lib64/pkgconfig /usr/share/pkgconfig /usr/lib/pkgconfig -name "*lz4*" 2>/dev/null # 4. 看头文件和动态库是否存在 ls /usr/include/lz4*.h /usr/lib64/liblz4* 2>/dev/null按我这个真实场景来模拟一下输出结果:
- 第 1 步显示:
lz4-1.8.3-3(运行时包,装是装了) - 第 2 步报错:
Package liblz4 was not found in the pkg-config search path. - 第 3 步没有任何输出
- 第 4 步能看到
/usr/lib64/liblz4.so.1.8.3,但看不到lz4.h
这个结果基本就锁定问题了:运行时库在,编译开发文件缺失。整个过程用不到一分钟,但能让你在后续操作时心里有底。
3.2 第二步:安装开发包并重新 configure
诊断完,直接安装:
dnf install -y liblz4-devel如果仓库没启用,先按上一节讲的启用everything再装。装完之后验证:
pkg-config --modversion liblz4 pkg-config --cflags --libs liblz4两条命令都正确输出后,回到编译目录,清掉缓存重新跑 configure:
make clean 2>/dev/null; make distclean 2>/dev/null ./configure --prefix=/usr/local/myprog注意一个很容易被忽略的细节:如果你是在configure报错之后才新装了开发包,尽量重新解压源码包或至少执行make distclean。因为configure在报错前已经生成了一些缓存变量(config.cache),这些缓存可能记住了“liblz4 没找到”的结果,直接再跑一次./configure有时会跳过检查,甚至延续失败。删掉config.cache文件也是一个有效操作:
rm -f config.cache ./configure ...这一步做完,checking for liblz4... yes就该出现了。
3.3 第三步:编译安装,而编译过程的潜在问题一个都别放过
configure通过之后,按理说就进入常规编译流程了:
make -j$(nproc) make install但 OpenEuler 上编译,尤其是某些用较新 GCC 版本的项目,可能还会冒出其他小毛病。比如我在编译一个用到 lz4 的存储工具时,make阶段报过warning: implicit declaration of function 'LZ4_compress_default',原因就是头文件路径没配对,但 configure 已经通过了,说明.pc文件的Cflags路径和头文件实际安装路径存在偏差。这种情况可以手动设置CPPFLAGS=-I/usr/include(或对应的包含路径)重新 configure。
还有一个注意点:如果/usr/lib64和/usr/lib里同时有不同版本的 liblz4,链接器选哪个取决于编译脚本里的搜索顺序,一般优先/usr/lib64。为了避免运行时动态链接加载到不匹配的旧库,安装完新版本后建议执行一次:
ldconfig这句命令会刷新动态链接缓存,让新安装的库文件正确登记。如果跳过,某些老系统上可能出现“编译通过、运行时报找不到liblz4.so.1”的诡异问题,而ldconfig能直接消灭这个隐患。
4. 如果dnf install liblz4-devel也救不了你:离线包、源码编译与自定义前缀
4.1 离线环境下怎么在不联网的 OpenEuler 上解决依赖
很多服务器出于安全考虑不能直接访问外网,这时候dnf install会失败。可行的办法有两个:一是在另一台联网的、系统版本和架构完全一致的机器上下载 RPM 包,再拷贝过去安装;二是从安装镜像本身提取 RPM。
在联网机器上下载依赖包的推荐命令是:
dnf install --downloadonly --downloaddir=/tmp/lz4rpms liblz4-devel注意,--downloadonly只下载不安装,但会连带把依赖包一起下到指定目录。如果这台机器上已经有部分依赖,可以加上--resolve参数强制解析完整依赖树。下载完之后,用scp或 U 盘拷到目标机器:
rpm -Uvh /tmp/lz4rpms/*.rpm如果系统里已有旧版库文件,用rpm -Uvh会升级;如果没有任何相关包,用rpm -ivh也可以。这里有个经验之谈:安装 RPM 时尽量把目录下所有.rpm文件一起装,别只装一个,不然依赖解析失败会搞得你怀疑人生。
4.2 源码编译 liblz4 的正确姿势
如果连 RPM 包都弄不到,那就只能走源码编译这条路了。lz4 的源码可以从官方仓库或发行版源码包获取。OpenEuler 的源码包地址通常可以在 gitee 镜像或官方软件包源里找到。我整理一下源码编译的关键步骤:
# 下载源码并解压 wget https://github.com/lz4/lz4/archive/refs/tags/v1.9.4.tar.gz tar xzf v1.9.4.tar.gz cd lz4-1.9.4 # lz4 的 Makefile 支持直接安装到系统路径 make -j$(nproc) make install注意 lz4 的默认安装路径是/usr/local,也就是说.pc文件会放在/usr/local/lib/pkgconfig/liblz4.pc。而绝大多数发行版(包括 OpenEuler)的pkg-config默认不搜索/usr/local/lib/pkgconfig(除非当时编译 pkgconf 时配置过),所以源码编译完还得手动设置环境变量:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH然后把这两个 export 写进/etc/profile.d/lz4.sh,否则新开的 shell 里依然找不到。实测下来,这是一整条链路里最容易遗漏的环节——我最初编译完 lz4 之后直接回去跑configure,又看到一模一样的checking for liblz4... no,当时真的想砸键盘,原因就是把PKG_CONFIG_PATH忘了。
如果头文件装在/usr/local/include,某些项目的 configure 也不搜索这个路径,这时候需要:
export CPATH=/usr/local/include export LIBRARY_PATH=/usr/local/libCPATH和LIBRARY_PATH这两个变量分别影响编译阶段头文件查找和链接阶段库文件查找,是源码安装软件后常见的补充配置。
4.3 连源码都编译不了?最后的手段是手工指定
还有一部分项目在configure阶段提供了--with-liblz4或LZ4_CFLAGS/LZ4_LIBS这类手工变量。比如你从源码编译了一个自定义路径的 lz4,可以不走pkg-config,直接告诉 configure 该用哪个头文件和链接库:
./configure LZ4_CFLAGS="-I/opt/lz4/include" LZ4_LIBS="-L/opt/lz4/lib -llz4"这种方式适合那些不依赖pkg-config的 autoconf 项目,或者你实在不想折腾环境变量的情况。但要注意:用了手工指定之后,pkg-config路径可以依然不满足,但configure的硬检查会通过,因为有些项目是“先试 pkg-config,失败后再看手工变量”。
5. 进阶排查:当Package requirements出现在更复杂的依赖链里
5.1 间接依赖导致的连环缺失:不只 liblz4 一个坑
单换一个 liblz4-devel 还算简单,更多时候你面临的是“解决了一个报错,下一个报错立刻冒出来”的连环局面。比如编译某些虚拟化工具(如 libvirt-daemon-kvm,这也是 OpenEuler ARM 服务器上的常见需求)时,依赖链往往包含liblz4、libxml2、libyajl、libpciaccess等等。每解决一个依赖,configure 就往后走一步,然后卡在下一个。
我的习惯是用下面的命令一次性装上一批常见的编译依赖基础包:
dnf install -y gcc gcc-c++ make autoconf automake libtool pkgconf \ liblz4-devel zlib-devel libxml2-devel openssl-devel \ libyajl-devel libpciaccess-devel numactl-devel注意这里的zlib-devel和liblz4-devel都属于同一类“压缩库开发包”,在很多项目里是一起被pkg-config检查的。把常用基础包一次装齐,能省掉大量反复 configure 的时间。
5.2 特殊场景:交叉编译和 32 位库的路径差异
如果你在做嵌入式或 ARM 开发,OpenEuler 上可能还需要支持 32 位交叉编译,那pkg-config的路径会更麻烦。一个稳妥的方案是使用PKG_CONFIG_LIBDIR指定目标平台的.pc文件目录,而不是简单加到PKG_CONFIG_PATH:
export PKG_CONFIG_LIBDIR=/opt/toolchain/arm-linux-gnueabihf/lib/pkgconfig export PKG_CONFIG_SYSROOT_DIR=/opt/toolchain/sysrootPKG_CONFIG_LIBDIR的含义是“只从这些路径找”,而PKG_CONFIG_PATH的含义是“额外加上的查找路径”,两者叠加容易造成混乱。交叉编译时别用PKG_CONFIG_PATH指宿主机的.pc文件,否则很可能链接到宿主机库,导致运行时目标板缺库。
还有一个小技巧:在 configure 之前,可以用pkg-config --variable=libdir liblz4查看这个库预期的安装目录,用它来验证你的交叉编译 sysroot 是否一致。如果不一致,就不要硬配pkg-config,改成手工指定LZ4_CFLAGS和LZ4_LIBS更靠谱。
5.3 排查工具推荐:用pkg-config的调试模式找到问题的最后一公里
pkg-config自带一个隐藏调试功能,PKG_CONFIG_DEBUG_SPEED这个环境变量听起来像玩笑,但实际上能输出详细的 search path 列表和查找过程:
PKG_CONFIG_DEBUG_SPEED=1 pkg-config --libs liblz4如果这个变量名在你的 pkgconf 版本上不支持,另一个更通用的方法是加--verbose参数。虽然pkg-config --verbose --libs liblz4在部分版本上输出有限,但至少能确认它进了哪条路径。也可以直接看 config.log:
grep -n "liblz4" config.log | head -20config.log是 configure 失败时留下的完整日志,里面会有pkg-config实际执行时的环境和变量。我的经验是优先看这个文件,比在终端反复猜原因高效得多。有一次我遇到PKG_CONFIG_PATH明明设置了却被忽略,最后就是在config.log里发现 configure 脚本设置了PKG_CONFIG_PATH覆盖了我的值。
6. 从这次排错里沉淀下来的几个实操习惯
最后分享几个我过去几年跟 configure 依赖问题打交道攒下的习惯,希望能帮你少走弯路。
第一,装任何开发包之前先想清楚自己缺的是“运行时库”还是“开发文件”。每次看到Package requirements报错,第一反应不是装库,而是打pkg-config --modversion <包名>,确认到底缺没缺.pc文件。这个习惯能省下至少一半的无用操作。
第二,养成看config.log的习惯。很多新手报错后只盯着终端最后几行,但config.log里是 configure 失败的“病历”,记录了检测命令、环境变量、编译器输出等所有细节。遇到任何Package requirements问题,先打开 config.log 搜索报错的包名,看看有没有关键线索。
第三,OpenEuler 上用 dnf 装不到的包别急着放弃,先启用全部仓库再搜索一遍。everything仓库里塞着大量开发包,很多人不知道这一点,最后绕了远路去源码编译,其实一条命令就能解决。
第四,源码编译装的库,务必顺手把PKG_CONFIG_PATH和LD_LIBRARY_PATH写进/etc/profile.d/下的脚本。不写到全局环境变量里,下次新开的会话又会找不着,白白浪费时间。
结合这次 liblz4 的问题来看,一个看似无解的configure报错,本质上就是“开发包没装或没被找到”这一句话的事。搞懂了pkg-config的工作原理和 OpenEuler 的软件包组织方式,这类问题非但不难,反而能成为你理解整个编译体系的一个切入点。以后再看到Package requirements报错,先别慌,按上面这几步走,多半能在几分钟内定位到根因。