1. 一次源码编译的“下马威”:APR not found 是什么情况
兄弟们,装个 Apache(也就是 httpd)本来不是什么大事,但你要是走源码编译这条老路,十有八九会被一个报错卡得头皮发麻:
configure: error: APR not found. Please read the documentation.我第一次遇到这个报错的时候,心里是一万个问号:我把 httpd 的压缩包解压了,进了目录,执行./configure --prefix=/usr/local/httpd,结果它给我来一句“找不到 APR”,还让我去读文档。问题是,我用find / -name "apr*"也搜索过啊,明明系统里可能压根没这东西,或者装了我不知道。
这个报错说白了就是:httpd 源码包里其实不包含完整的 APR(Apache Portable Runtime)库,它默认假设你的系统里已经装好了 APR、APR-util 和 PCRE 这三个依赖。如果 configure 脚本在默认路径下找不到它们,就会直接摆烂退出,而不是帮你自动下载或者说“我帮你装一个”。跟鸡蛋里挑骨头似的,本质上是一个依赖缺失问题,但这背后牵扯到你对 Linux 软件安装机制、库文件搜索路径、版本兼容性的理解。
那这篇文章我就把从零开始解决这条报错的路子完整走一遍。不是只告诉你“yum install apr apr-devel”这么简单,而是把背后的排查逻辑、动静两派解决方式、还会踩到什么坑,都讲清楚。适合哪些人看?刚接触 Linux 源码编译的新手、公司内网环境没法随便联网装包的同学,以及想把 Apache 调教得明明白白的运维入门选手。
2. 先搞懂 httpd、APR、APR-util 各自是谁,才好对症下药
2.1 APR 是什么,为什么 httpd 非要它不可
APR 的全称是 Apache Portable Runtime,说白了就是 Apache 自己维护的一套跨平台底层运行时库,它把文件操作、网络 socket、进程管理、共享内存、线程锁这些杂七杂八的系统调用封装成一套统一的接口。
我们不需要每个 Linux 发行版上的网络 API 写一遍,只要针对这套接口适配了某个平台,上层应用就能顺利跑起来。没有 APR 的话,httpd 自身的一大堆功能就直接没法编译通过。
这里有个很容易被忽略的细节:APR 不是 Apache 服务器的功能模块,它是一个基础依赖库。就像你做饭需要先有锅和灶,APR 就是那口锅。报错“APR not found”并不是说你 Apache 装坏了,而是说它赖以生存的锅还没准备。
2.2 光有 APR 还不够,APR-util 和 PCRE 也是刚需
很多时候,你解决完“APR not found ”,马上又会遇到下一个报错:
configure: error: APR-util not found. Please read the documentation.或者:
configure: error: pcre-config for libpcre not found.这说明 configure 是按顺序检查依赖的:先检查 APR,再检查 APR-util,再检查 PCRE。你缺哪个它就先报哪个。所以铺路要一次铺齐,别挤牙膏似的查一个装一个。
- APR-util:在 APR 基础之上提供更上层的功能,比如数据库连接池、LDAP 客户端、XML 解析等等。httpd 的很多模块需要用到它。
- PCRE:Perl Compatible Regular Expressions 库,负责正则表达式解析。Apache 的配置指令里大量用到正则匹配(比如
<IfModule>、RewriteRule),没有 PCRE 很多规则没法用。
2.3 报错信息里没告诉你的那些隐含条件
看报错日志的时候,光看最后一行“APR not found”其实信息非常有限。更好的习惯是把 configure 的输出从头到尾扫一遍,你会看到它其实还打印了:
checking for APR... no也就是它检查过了,结果是否定的。再往上翻,可能还有警告信息,比如:
configure: WARNING: APR version 1.5.0 or later is required这种情况是系统里有 APR,但版本太老,压根不达标。你敢信,有些老系统自带的 APR 版本停留在 1.4 左右,而新版本 httpd 对 APR 版本有下限要求。所以“找不到”往往不只是不存在,也可能是版本不够。
3. 两条解决路线:包管理器省心法 vs 源码编译折腾法
3.1 路线一:用发行版自带的包管理器装依赖(推荐新手上路)
这是最省心的一种方式。不同发行版对应命令不一样:
CentOS / RHEL / Rocky Linux 系:
yum install -y apr apr-devel apr-util apr-util-devel pcre pcre-develDebian / Ubuntu 系:
apt-get update apt-get install -y libapr1 libapr1-dev libaprutil1 libaprutil1-dev libpcre3 libpcre3-dev装完之后,再回来执行:
./configure --prefix=/usr/local/httpd一般情况下,configure 就能顺利通过。为什么这里要强调-devel或-dev结尾的包?因为 httpd 编译时不光需要 APR 运行库,它还需要头文件(header files),比如apr.h、apr_pools.h这些。普通的apr包只提供运行时需要的动态库(.so),不提供开发用的头文件;apr-devel才提供头文件和链接时的元数据。
提示:如果你在最小化安装的 CentOS 环境里,
yum install可能会提示你“No package apr-devel available”。这种情况十有八九是你没装 EPEL 源或者没启用 BaseOS 源,先处理仓库源问题再继续。
3.2 路线二:源码方式自己编译安装 APR(适合离线内网或自定义版本)
包管理器虽然方便,但在某些场景下并不好使:
- 服务器在内网,没法直接连外网仓库;
- 默认仓库里的 APR 版本太老,不满足 httpd 要求;
- 公司安全规范要求所有软件必须走内部编译发布流程。
那就得自己找 APR 源码包来编。官方下载渠道一般是 Apache 软件基金会提供的镜像站,比如:
- APR 源码包:
apr-1.7.x.tar.gz - APR-util 源码包:
apr-util-1.6.x.tar.gz - PCRE 源码包:
pcre2-10.x.tar.gz(新版 httpd 对 pcre2 的支持更好,建议直接用 pcre2)
下载完成之后,按顺序编译安装。先说 APR:
tar -zxvf apr-1.7.4.tar.gz cd apr-1.7.4 ./configure --prefix=/usr/local/apr make && make install这里--prefix=/usr/local/apr是建议的路径,把 APR 独立放在一个目录,方便后续管理和卸载。不指定也行,默认会装到/usr/local/apr的变异路径下,但那样找起来费劲,我建议还是显式指定。
接下来编译 APR-util。注意,它编译时需要知道 APR 的安装位置,所以要用--with-apr参数:
tar -zxvf apr-util-1.6.3.tar.gz cd apr-util-1.6.3 ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr make && make install然后编译 PCRE2:
tar -zxvf pcre2-10.42.tar.gz cd pcre2-10.42 ./configure --prefix=/usr/local/pcre2 make && make install编译完这三个之后,再回到 httpd 源码目录,配置的时候把它们的路径都指过去:
./configure --prefix=/usr/local/httpd \ --with-apr=/usr/local/apr \ --with-apr-util=/usr/local/apr-util \ --with-pcre=/usr/local/pcre2这一路参数就是明确告诉 configure:别瞎找了,APR 在这、APR-util 在那、PCRE 在这,直接拿去用吧。
3.3 两种路线的利弊分析
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 包管理器安装 | 快、简单、自动处理依赖关系 | 仓库版本可能偏旧;自定义安装路径困难 | 能联网、对版本要求不高的场景 |
| 源码编译 APR | 版本可控、路径可控、适合离线环境 | 步骤多、容易踩版本兼容性问题 | 内网服务器、需要自定义参数、追求新版本 |
诚心建议是,能走包管理器就别折腾源码,除非你有强制性要求。MySQL、PHP 都可以编译安装玩,但 APR 这种底层库,自己编一次就会发现,它居然还有交叉编译、静态链接、共享库路径配置一堆破事,投入产出比很低。
4. 实操现场:源码编译 APR 全流程记录与避坑细节
4.1 步骤一:准备编译工具链
先别急着下载源码,你得确保系统里有编译器。用官方点的说法就是 gcc、make、libtool 这些基础组件。
CentOS/RHEL:
yum groupinstall "Development Tools"Ubuntu/Debian:
apt-get install build-essential libtool-bin值得注意的一点是,APR 的 configure 脚本生成过程可能依赖libtool的特定版本。如果你在 Ubuntu 上用的是libtool-bin这个包,还得确认一下版本。我遇到过在较新系统上编译老版本 APR 时,libtoolize因为版本不匹配导致生成出来的 makefile 有问题的坑。
4.2 步骤二:编译 APR 时的常见报错与对应处理
先看一个高频报错:
/usr/bin/ld: cannot find -luuid collect2: error: ld returned 1 exit status这是链接器找不到 uuid 库。解决办法:
- CentOS:
yum install -y libuuid-devel - Ubuntu:
apt-get install -y uuid-dev
其实源码方式编译 APR,最烦的就是这种“配置通过了,结果 make 时炸了”的情况,缺乏经验的容易在这里懵。
还有一个高频报错:
error: libtool: link: cannot find the library `-lrt` or `-lpthread`这个通常不是缺库,而是工具链没装全。把 gcc、g++、make、libtool、autoconf 这些全装上,一般能解决。另外有些老教程会让你加LDFLAGS=-lrt来硬编,但那是治标不治本。
操作心得:在编译 APR 之前,先跑一句
ldconfig -p | grep -E "libuuid|libcrypto|libexpat",大概扫一眼系统里有没有这些常用依赖库。缺啥提前补上,比等 configure 报错再回来查效率高得多。
4.3 步骤三:配置 httpd 并处理后续连环报错
当你顺利把 APR、APR-util、PCRE 都编译安装好,进入 httpd 的./configure时,可能还会遇到一个新的经典报错:
configure: error: Could not find a version of the library libpcre明明刚才不是编译了 pcre2 吗,怎么还找不到?
原因很简单:httpd 某些老版本用的是 PCRE1,不是 PCRE2。如果你编译的是 PCRE2,但 configure 脚本还停留在找 PCRE1 的阶段,它自然搜不到。
解决办法有两条:
- 下载 PCRE1 的最终版本
pcre-8.45.tar.gz,传进系统里编译安装,再通过--with-pcre=/usr/local/pcre指定路径; - 或者用比较新、支持 PCRE2 的 httpd 版本源码(httpd 2.4.58 之后对 PCRE2 友好很多)。
我当时在老版本 httpd 上被这个问题卡了一个多小时,后来干脆换了新版本源码,同时换 pcre2 编译,一路畅通。所以给大家的建议是:源码编译时,版本组合是重中之重,别老想着用老掉牙的 httpd 配新依赖库。
4.4 自定义路径场景下的环境变量配置
如果你把 APR 装在/usr/local/apr,配置完 httpd 是没问题了,但运行 httpd 的时候可能出幺蛾子,因为它运行时需要找到libapr-1.so.0这个动态库。而系统默认的库搜索路径里往往没有/usr/local/apr/lib,于是你会看到:
error while loading shared libraries: libapr-1.so.0: cannot open shared object file: No such file or directory这个问题的本质是动态链接器找不到共享库。解决办法:
echo "/usr/local/apr/lib" > /etc/ld.so.conf.d/apr.conf ldconfigldconfig会重新生成/etc/ld.so.cache,让系统以新路径查找共享库。这一步非常容易忘,一旦忘了,前面所有编译的喜悦都会在启动 httpd 时被兜头浇灭。
5. 从“装好依赖”到“真正编译成功”的关键细节
5.1 configure 到底在检查什么
很多人对 configure 的印象就是“一条命令,运行完就完事”,其实它在背后做了大量探测工作。当你执行./configure --prefix=/usr/local/httpd时,它执行了以下几个与 APR 相关的核心检查:
- 查找指定路径下的
apr-config脚本; - 执行
apr-config --version来验证版本号; - 执行
apr-config --cppflags --ldflags --libs来获取编译参数; - 尝试编译一个小的可执行文件,链接 APR 库,验证编译链路的可行性。
如果上面任何一步失败,configure 就会给出那个经典的“APR not found”。
理解了这个流程后,你就知道:它说 not found,很可能不是文件不存在,而是你在源码编译时没有把自定义路径传给它。因为默认搜索路径里根本没有/usr/local/apr/bin。
所以,如果你用--with-apr传参之后还是报错,可以手动跑一下:
/usr/local/apr/bin/apr-config --version看看这个命令能不能正常输出版本号。如果没有输出或者提示找不到命令,那说明你的 APR 编译安装过程本身就有问题。
5.2 从源码包还是从开发包安装,二者差异很大
不少新手会把apache2-dev、httpd-devel、apr-devel这几个包搞混。它们有的是 Apache 本体,有的是 Apache 的配套开发包,有的是 APR 的开发头文件包。
在 Debian/Ubuntu 系统上,如果你执行:
apt-get install -y apache2那只是安装了 Apache 运行环境,并不一定会把libapr1-dev装进来,所以当你再去用源码编译另一个 httpd 时,还是可能遇到“APR not found”。
正确的做法是用apt-cache search apr看看有哪些包可用,然后安装对应的 dev 版本:
apt-cache search apr | grep -i dev这相当于在“阅兵”一样,先看看仓库里有哪些兵种,再决定派哪支部队上。
5.3 版本匹配问题:新 httpd 配老 APR 的隐性风险
假设你系统自带的 APR 是 1.6.5 版本,而 httpd 是 2.4.62 最新版。理论上没问题,因为 httpd 2.4.x 本来就要求 APR 1.6+。但如果你用的是 APR 1.4.x,那么很多新特性就不支持,configure 阶段也可能直接报错。
这里给一张对照表,方便大家参考:
| httpd 版本 | 最低 APR 版本 | 最低 APR-util 版本 | 最低 PCRE 版本 |
|---|---|---|---|
| httpd 2.2.x | APR 1.2 | APR-util 1.2 | PCRE 6.0 |
| httpd 2.4.x | APR 1.5 | APR-util 1.5 | PCRE 8.0(推荐 8.40+) |
| httpd 2.4.58+ | APR 1.6 | APR-util 1.6 | PCRE2(支持但有时需配置) |
不要小看这个表,很多生产环境里“明明按教程做却失败”的怪现象,最终排查下来都是版本之间差了一截。
6. 所有踩过的坑:常见问题排查速查表
收集我及身边同事在 Apache 编译安装过程中经常遇到的典型问题,整理成一个速查表。
问题一:执行 configure 直接报 APR not found
- 原因 1:系统里压根没装 APR。解决:用包管理器安装,或源码编译。
- 原因 2:装了 APR 但未安装开发包(-devel / -dev)。解决:补装对应 dev 包。
- 原因 3:APR 装在了非默认路径。解决:给 configure 传
--with-apr参数。 - 原因 4:APR 版本过低。解决:升级到 httpd 要求的版本。
问题二:configure 提示 APR-util not found
- 原因:APR-util 缺失或路径不对。解决:参考上文路线一或路线二,在安装 APR 之后安装 APR-util。注意 APR-util 编译时需要
--with-apr指定 APR 路径。
问题三:configure 提示 pcre-config for libpcre not found
- 原因:PCRE 开发库缺失。解决:安装
pcre-devel/libpcre3-dev,或手动编译 PCRE 后通过--with-pcre指定路径。 - 注意事项:如果你安装的是 PCRE2,但 configure 还在找
pcre-config(PCRE1 的命令),可以先建一个软链接:
ln -s /usr/local/pcre2/bin/pcre2-config /usr/local/bin/pcre-config但这不是长久之计,最好还是让 httpd 版本匹配 PCRE 版本。
问题四:make 时报错找不到头文件
- 原因:比如
apr.h找不到。这一般是在编译 httpd 时,APR 的头文件路径没有传递进去。 - 解决:确认
--with-apr参数指向的目录里存在include/apr-1/apr.h,然后用下面的方式把 CPPFLAGS 传给 configure:
CPPFLAGS="-I/usr/local/apr/include/apr-1" ./configure --with-apr=/usr/local/apr ...问题五:make install 成功但启动时报错缺少动态库
- 原因:动态库路径不在系统搜索范围内。
- 解决:参考 4.4 节,修改
/etc/ld.so.conf.d/下配置文件并执行ldconfig。
问题六:编译过程中报libtool相关错误
- 原因:系统的 libtool 版本太老或没装。
- 解决:CentOS 下
yum install -y libtool;Ubuntu 下apt-get install -y libtool-bin。如果还不行,建议源码编译安装新版 libtool。
这张表建议收藏,遇到问题不要慌,从上到下排查一遍基本能搞定。
7. 这套报错里藏着的 Linux 底层逻辑,值得你多读两遍
7.1 动态链接、头文件、开发包,三者是老三样
这个 APR not found 的报错,看起来只是一个小问题,但它内部把 Linux 开发环境里几个最基础的概念串联了起来:
- 头文件(headers):编译 C 代码时,编译器需要看函数的声明。APR 提供了比如
apr_file_open这种函数的声明,就放在apr_file_io.h里。没有头文件,编译器直接报“隐式声明”或找不到定义。 - 动态链接库(shared libraries):程序运行时,需要通过
dlopen或动态链接器加载.so文件。Linux 系统里运行时代码会从/usr/lib、/usr/lib64、/usr/local/lib等路径去搜索。 - 开发包(-devel / -dev):把上面两样打包发布的一个规范。普通运行包只放
.so,开发包会额外放头文件和.so的符号链接文件(比如libapr-1.so -> libapr-1.so.0.7.0)。
理解这老三样之后,很多 Linux 下的编译报错你都能一眼看穿:是找不到头文件、找不到库文件,还是找不到符号。而不是每次都去复制粘贴报错到搜索引擎里。
7.2 预处理、编译、链接:编译期和运行期为什么要分开看
一个 C 程序从源码到可执行文件,要经过预处理、编译、汇编、链接几个阶段。configure阶段的检查大多发生在编译早期和链接阶段:
- 如果报错是
fatal error: apr.h: No such file or directory,那是编译器没找到头文件; - 如果报错是
cannot find -lapr-1,那是链接器没找到库文件; - 如果报错是编译都通过但运行时提示找不到
.so,那是动态加载器没找到共享库。
这三个报错看起来很像,但解决思路完全不同。能不能分清楚,基本就界定了“会写代码的人”和“只会抄命令的人”。
回到 APR not found 这个报错,它就是链接 / 配置阶段出了问题,比编译阶段报错要更厚重一点,因为它往往是因为缺少依赖项而不是因为你的代码写错了。
7.3 为什么源码编译的软件难以卸载干净
最后再聊一个源码编译的痛点:卸载难。如果你是用yum install apr-devel装的依赖,卸载时你只要:
yum remove apr-devel但如果你是自己编译安装到/usr/local/apr的,卸载时通常只能:
rm -rf /usr/local/apr外加清理一下/etc/ld.so.conf.d/里的配置。更麻烦的是,如果当时有别的软件链到了这份 APR 上,那它也会跟着出问题。所以生产环境里,我还是推荐:
- 优先用发行版包管理器装依赖;
- 如果必须自己编,就把它单独放到独立目录,并在文档里记录清楚;
- 千万别图省事把所有东西装到
/usr/local/lib大杂烩目录里,管理会非常混乱。
8. 最后再分享两个小实操建议
关于这个 APR not found 报错,最后再补充两个我个人比较受用的习惯。
第一个是,在 configure 之前先加载环境变量。如果你已经把 APR 装到了自定义目录,与其每次命令里长长地传一堆--with-apr、--with-pcre,不如把这些路径统一整理成一个环境变量脚本httpd_env.sh:
export APR_HOME=/usr/local/apr export APR_UTIL_HOME=/usr/local/apr-util export PCRE_HOME=/usr/local/pcre2 export LD_LIBRARY_PATH=$APR_HOME/lib:$APR_UTIL_HOME/lib:$PCRE_HOME/lib:$LD_LIBRARY_PATH export CFLAGS="-I$APR_HOME/include/apr-1 -I$PCRE_HOME/include" export LDFLAGS="-L$APR_HOME/lib -L$PCRE_HOME/lib"然后每次编译之前:
source httpd_env.sh这样能避免你每敲一遍 configure 都要数一下到底漏了哪个参数。特别是后期还要给 httpd 添加模块,重新执行 configure 时,这个环境变量脚本能帮你避开很多参数拼写错误。
第二个是,测试阶段优先用默认安装路径跑通整个流程。很多初学者一上来就自定义--prefix=/usr/local/httpd-custom,导致后续日志路径、配置路径全都跟默认的不一样,排查问题时经常分不清到底用的是哪套配置。先让整个源码编译流程用默认路径完整走一遍,确认没问题之后,再按业务需求做自定义调整,这样排错时至少能确定问题一定在你自定义的那部分里,而不会怀疑到底是不是编译流程本身有问题。
Apache httpd 编译安装虽然只是一个入门级的技能点,但围绕它展开的动态库、头文件、编译链路、版本兼容性这些知识,在很多其他软件安装场景里都能复用,算是花一份时间,武装一套底层思维吧。