简介:资源围绕 libcurl 跨平台编译展开,适用于需要在 Linux、macOS、iOS、Android、Windows 平台集成 HTTP、FTP、SMTP 等网络通信能力的客户端开发者,也适合准备移植网络库、排查构建链问题的工程人员。压缩包共 3 个文件,3.82MB,包含 Markdown 编译说明、curl-7.74.0 源码压缩包和 iOS 交叉编译脚本:前者梳理接口调用与构建步骤,源码包提供完整代码环境,脚本示范如何为移动端自动生成目标库。已有 408 人学习下载。借助这套小体积资料,可快速掌握 configure、make、CMake、NDK 等典型构建方式,理解静态库与动态库在不同平台下的差异,并延展到 SSL/TLS 后端选择、架构适配等实操细节。无论做桌面端还是移动端网络层,都能据此少走弯路,直接提升 libcurl 的集成效率。 前阵子我需要把 libcurl 编译成 Windows、Linux、macOS 三套平台的产物,最后还要整理成一个干净的 curl.zip 分发给不同环境的测试机。整个过程看起来就是“下载源码、敲几条命令、打个包”,但实际做下来,光依赖选择、SSL 后端、动态链接库带全这三件事就折腾了不少时间。这篇就把我走通的流程、关键参数和踩坑点完整记录下来,内容同时覆盖 curl.exe 和 libcurl 库,适合正在做跨平台编译、需要在离线环境部署 curl、或者准备把自己的下载模块接进 libcurl 的开发者参考。
1. 为什么坚持自己编译 libcurl,而不是用现成包
1.1 先想清楚:你要编译的是 curl 还是 libcurl
curl 在社区里通常混指两个东西:一个是命令行工具 curl.exe,另一个是 C 语言库 libcurl。很多应用其实只需要其中的一部分:比如我这次的场景,测试环境里要一个能在脚本里调用的 curl.exe,同时另一个 C++ 项目需要把 libcurl 静态编进去,做成下载模块。所以目标和形态必须在编译前就锁定:要命令行,BUILD_CURL_EXE 就打开;要库,BUILD_SHARED_LIBS 决定动态还是静态。搞混了这两点,后期会浪费非常多时间。
1.2 什么场景下非自己编译不可
操作系统自带的 curl 往往不满足需求。最常见的情况是:目标机器没有外网,必须离线分发一个 curl.zip;或者项目要求用 OpenSSL 而不是系统默认的 GnuTLS/Schannel;再或者要裁剪协议,仅保留 HTTP/HTTPS/FTP,减小体积。还有一个容易被忽略的场景:程序里调 libcurl,但 CentOS 7 这类老系统的 curl 库版本太旧,接口缺失,只能自己编一个新版静态库喂给项目。自己编译的意义就在于可控,编译选项、依赖版本、协议支持、产物目录都可以按着需求定制。
1.3 自编译能换来什么,又要付出什么
换来的东西很直观:一个完全由你决定的二进制。付出的代价则是要管理工具链和依赖。跨平台这件事的复杂度,主要不在 autotools 和 CMake 的写法差异,而在“每个平台的依赖库怎么找、怎么编、怎么链进去”。如果需求只是本机用,apt 或 brew 一条命令解决完全够用;一旦涉及分发、定制和多平台,就只能把编译流程自己握在手里。实测下来最省心的做法,是把三套平台的编译命令统一写成脚本,固定参数,后续重新发布时只需要改版本号,其他不用动。
2. 编译前先搞定源码、依赖和工具链
2.1 源码从哪里拿、版本怎么选
官方源码托管在 GitHub,也提供 release tarball。我这次选的是 7.71.1 的 release 包,这个版本不算最新,但非常稳,很多发行版和嵌入式项目都拿它做基线。如果你是新项目,我更推荐用当前最新的稳定 release,不要直接拉 master,除非你想顺手体验开发分支的不确定性。下载后先校验一下 sha256,避免拿到被串改过的包。这一步在正式发布流程里不是可选项,哪怕只是内部自用,也应该保留一份校验记录,方便后续追溯。
2.2 依赖库怎么取舍:OpenSSL、zlib、c-ares
依赖是跨平台编译第一个分水岭。默认的 curl 也可以不依赖 OpenSSL,在 Windows 上它原生支持 Schannel,在 Linux 上支持 GnuTLS,但如果你要分发到老系统、或对接特定 HTTPS 服务,OpenSSL 往往是兼容性最好的选择。zlib 看情况,只要代码里可能处理压缩内容就建议带上。c-ares 的作用是异步 DNS 解析,对并发请求有帮助,但不是必需品;除非明确要做高并发,否则可以先不引入,省一堆交叉编译麻烦。这些依赖库必须先于 curl 编译好,并且用同一套工具链,特别是 Windows 上使用不同编译器版本编译的第三方库,静态链接时容易出现符号冲突。
2.3 为什么 CMake 是跨平台编译的主线
curl 从很早就同时支持 autotools 和 CMake。autotools 在 Linux/macOS 上很顺,configure && make && make install 几行命令就能完成,但在 Windows 上,虽然有 MSYS2 和 Cygwin 这类兼容层,体验依旧不够直接。CMake 的好处是同一份构建脚本在三个平台都能产出对应当地工具链的工程或构建产物。我在三个平台统一以 CMake 作为第一选择,Linux 上再用 autotools 做一次对照验证,也比较省心。所谓“预编译的写法”,本质上是把常用参数固定到一个脚本文件里,而不是每次敲一长串命令,避免遗漏关键开关。
3. 三套平台的实际编译过程
3.1 Windows:用 CMake 生成 Visual Studio 工程
Windows 上最省心的路径是用 CMake 生成 VS 解决方案再编译。假设源码头已经放在D:\curl,依赖目录在D:\deps,大致命令:
cd D:\curl cmake -B build-win -S . ` -DBUILD_CURL_EXE=ON ` -DBUILD_SHARED_LIBS=OFF ` -DCURL_USE_OPENSSL=ON ` -DCURL_USE_SCHANNEL=OFF ` -DOPENSSL_ROOT_DIR=D:\deps\openssl ` -DZLIB_ROOT=D:\deps\zlib ` -DCMAKE_INSTALL_PREFIX=D:\curl\dist\win cmake --build build-win --config Release --parallel 8 cmake --install build-win --config Release这里我把 BUILD_SHARED_LIBS 关掉,是为了得到静态 libcurl,方便 C++ 项目直接链入,同时免去目标机器上带 DLL 的麻烦。如果只是要一个 curl.exe,其实开不开动态库影响不大。OPENSSL_ROOT_DIR 必须指向正确的依赖目录,这一步是最容易错的地方:CMake 有时会找不到 OpenSSL,最后退回去用 Schannel,但你命令行里写的却是 OpenSSL,结果就是编出来的 curl 跟预期不符。
编译完成后,产物会出现在D:\curl\dist\win,bin 下面是 curl.exe,lib 下面是 libcurl.lib,include 下面是头文件,还有一堆 cmake 辅助文件。这些都需要打包进 curl.zip。
3.2 Linux:configure 与 CMake 对照
Linux 上我习惯先用 autotools,因为 curl 的 configure 脚本在 Linux 上写得最成熟:
./configure --prefix=$PWD/_install \ --with-openssl \ --enable-static \ --disable-shared \ --enable-threaded-resolver make -j$(nproc) make install--enable-threaded-resolver对应多线程解析方案,不引入额外依赖也能做并发 DNS 查询。静态编译的好处在这里体现得很充分:编译出的 curl 可以拷贝到同体系的其他 Linux 机器上直接运行,不用担心大部分共享库缺失。比较老的一些发行版,glibc 版本如果太低,静态二进制也会遇到符号找不到的问题,但绝大多数现代系统没问题。
作为对照,CMake 的流程基本一致:
cmake -B build-linux -S . -DCMAKE_BUILD_TYPE=Release \ -DBUILD_CURL_EXE=ON -DBUILD_SHARED_LIBS=OFF \ -DCURL_USE_OPENSSL=ON -DCMAKE_INSTALL_PREFIX=$PWD/_install cmake --build build-linux -j$(nproc) cmake --install build-linux两种方式产出的 curl 在功能上区别不大,但是 CMake 生成的工程更容易和 IDE 配合,比如在 CLion 里直接调试 libcurl 的行为。
3.3 macOS:SDK 版本与证书链的坑
macOS 上编译和 Linux 接近,但有几个点要单独注意。第一是 SDK 版本:用 Xcode 自带 Command Line Tools 时,CMake 会自动探测 SDK,但如果同时装了多个 Xcode 版本,需要先固定编译器环境。第二是证书链:macOS 默认用 SecureTransport,不做额外的证书管理,很多程序会奇怪“为什么在这台 Mac 上 curl 突然报证书错误”,本质是 SecureTransport 与系统钥匙串的配合和 OpenSSL 不同。相对省心的做法仍然是显式指定 OpenSSL,保持与 Windows/Linux 一致的行为:
cmake -B build-mac -S . -DCMAKE_BUILD_TYPE=Release \ -DBUILD_CURL_EXE=ON -DBUILD_SHARED_LIBS=OFF \ -DCURL_USE_OPENSSL=ON -DCMAKE_INSTALL_PREFIX=$PWD/_install cmake --build build-mac -j$(sysctl -n hw.ncpu) cmake --install build-mac这里有句实在话:在 macOS 上使用 OpenSSL 静态库,发布前最好在干净的虚拟机里过一遍。因为本机可能装了很多自定义证书,会掩盖真实环境里的证书链问题。
4. 把三套产物整理成 curl.zip:发布与部署的坑
4.1 curl.zip 里应该有什么
我最终的发布包通常不是单纯把 exe 丢进去,而是按开发包和运行包分开设计。如果分发给脚本和命令行用户,只需要 bin 下的 curl 可执行文件;如果是给其他开发者对接 libcurl,就必须带上 include 目录、lib 静态库或动态库、cmake 配置文件。一个比较标准的目录:
curl/ bin/curl.exe include/curl/*.h lib/libcurl.lib lib/libcurl.a lib/cmake/CURLConfig.cmake share/man/man1/curl.1在 macOS 上还有libcurl.dylib,Linux 上可能是libcurl.so。如果你编译的是动态库版本,那么必须把动态库一起放进去,并放到系统能找到的路径。
4.2 依赖 DLL 和动态库怎么带全
这是最容易把 curl.zip 变成废包的环节。Windows 上如果用了共享依赖库,curl.exe 本身可以编译出来,但运行时会报“找不到 libssl-3-x64.dll”。这不是 curl 的问题,是 PATH 的问题。打包前用dumpbin /dependents curl.exe或objdump -p curl.exe | grep DLL看看依赖,把 OpenSSL、zlib 的 DLL 一起放进 bin 目录。在 Linux 上同样的检查工具是ldd curl,如果你用了非系统路径的库,要注意 rpath 或LD_LIBRARY_PATH。我建议静态链接尽量多开,能少带一个动态依赖,分发时就少一个崩溃点。
4.3 打包后的基本验证
拿到 curl.zip 后,先别急着发给别人,先做一个最小验证:
./curl --version ./curl -I https://example.com ./curl --http2 -I https://example.comWindows 上注意要在 cmd 里跑第一条,避免 PowerShell 的./curl解析问题。如果--version输出的协议列表里没有 https,说明你的 SSL 依赖没编进去,回去查编译选项。再用一个带重定向的 URL 测试,保证跟随跳转和证书校验都正常。实测下来,很多自制包死在了第一步:版本号能输出,但 https 协议缺失,这种包在线上会非常难看。
5. 常见编译错误与排查技巧
5.1 curl: (35) schannel 握手失败的真相
这个错误通常发生在 Windows 上使用 Schannel 后端时。比如你编出的 curl 访问某个 SSL 服务,返回curl: (35) schannel: next initialize security context failed: SEC_E_INVALID_TOKEN。这个报错第一反应是系统时间、CA 证书库的问题,但还有一个很容易被忽略的原因:Schannel 与某些服务器的 TLS 配置兼容性并不总是最好,特别是遇到对端要求特定 TLS 版本或客户端证书校验策略时。作为编译者,最简单的止损方案是把后端切到 OpenSSL,也就是前面 Windows 命令里的DCURL_USE_SCHANNEL=OFF;如果必须保留 Schannel,则需要统一更新系统证书库,并检查目标 URL 是否对 TLS 版本有要求。
5.2 curl: (6) couldn't resolve host name 的隐藏前提
curl: (6) couldn't resolve host name for https://mirrors.almalinux.org这类错误比看起来复杂。如果是自己编译的静态 curl,先确认编译时有没有启用解析器相关选项,以及系统 DNS 配置是否正常。Linux 下可以检查/etc/resolv.conf,Windows 下用nslookup验证域名解析是否本身失败。还有一类隐蔽原因:交叉编译时,libc 的解析器实现和运行环境不一致,在目标机器上无法正确调用 getaddrinfo。这种情况在容器里容易遇到,因为容器里的/etc/resolv.conf和宿主机不一样。先跟踪解析流程,基本能定位到是编译问题还是网络环境问题。
5.3 编译期的其他高频报错怎么处理
最近群里好几个人在问 QML 工程编译时报“找不到 curl”,其实问题不在 QML,而是 Qt 项目里链接了 libcurl 后,链接器找不到对应的库文件。类似的情况也出现在 cpprestsdk、libsamplerate、qscintilla 这类第三方库的编译中。处理逻辑都一样:先检查CMAKE_PREFIX_PATH或LIBRARY_PATH是否指向你自编译的 curl 安装目录;再看目标程序是 Debug 还是 Release,因为 Windows 下 Debug 和 Release 的 libcurl 静态库如果混用,经常出现无法解析的外部符号__imp_curl_easy_init之类的问题。简单说,如果这个错误不是 curl 本身造成的,而是项目集成时的链接顺序问题,那多半要先排查依赖顺序,把 libcurl 放在被依赖的一方之后。
我在实际发布中还有一个小习惯:不管哪个平台,编译前先把版本和编译参数写进一个build-info.txt,和 curl.zip 一起发出去。这样测试那边反馈问题时,我能第一时间知道对方手里的包是基于什么选项编出来的,排查效率会高很多。如果你是第一次做 libcurl 跨平台编译,建议从 Linux 和 Windows 两个平台开始,跑通之后再补 macOS,三套流程共通点很多,只要 CMake 的思路理清了,剩下就是依赖库路径和平台细节的差异。
本文还有配套的精品资源,点击获取