curl是一个成熟的HTTP client库,现在windows平台下可以使用cmake在命令行完成编译。
build curl
以下脚本实现Windows CMD下使用cmake及VS2015编译curl
cd curl
mkdir build
cd build
#############################################
# 生成Visual Studio 2015工程文件
cmake .. -G "Visual Studio 14 2015 Win64" ^
-DCMAKE_INSTALL_PREFIX=J:\curl-vc140-x64 ^
-DBUILD_SHARED_LIBS=OFF ^
-DCURL_USE_OPENSSL=ON ^
-DCMAKE_PREFIX_PATH=J:\skcapmini\dependencies\dist\openssl-1.1.0c\vc140\x64\release\dynamic;J:\skcapmini\dependencies
\dist\zlib-1.2.9\vc140\x64
# CMAKE_INSTALL_PREFIX 指定安装位置
# BUILD_SHARED_LIBS=OFF 生成curl静态库,如果生成动态库,可以不加此选项
# CURL_USE_OPENSSL=ON 指定使用OPENSSL,如果不需要支持HTTPS,可以不加此选项
# CMAKE_PREFIX_PATH 指定OpenSSL,ZLIB库的安装位置不需要支持HTTPS,可以不加此选项
#############################################
# 编译CURL并安装到CMAKE_INSTALL_PREFIX指定的位置
# 编译Debug版
cmake --build . --target install --config Debug -j 8
# 编译Release版
cmake --build . --target install --config Release -j 8
# - j 8 指定8线程并行编译,以提高编译速度
find_package
上一步已经编译了curl,现在就可以在自己项目的中引用curl库了
一般我们会在cmake脚本中通过find_package
查找curl库,
find_package(CURL)
一般来说这样也没问题,但如果你是像博主一样使用自己编译的curl静态库。那么应该以如下方式调用find_package
# 优先使用CONFIG模式查找
find_package(CURL CONFIG)
if(NOT CURL_FOUND)
# 没找到再用MODULE模式查找
find_package(CURL)
endif()
因为cmake本身提供了Module方式查找CURL的脚本,调用find_package时如果不指定CONFIG模式,默认是以MODULE方式调用cmake内置的FindCURL.cmake
来查找CURL。
不论是MODULE还是CONFIG模式都能正常找到CURL,你在项目中引用用CURL::libcurl target,能正常编译项目,但如果是要生成动态库或EXE等需要连接的动作时就会报错找不到openssl,zlib,socket通讯相关的函数。因为MODULE模式查找CURL时并没有把CURL静态库所依赖的openssl,zlib等库也加到CURL::libcurl target,所以导致连接失败。
正因为如此,要如上优先使用CONFIG模式查找CURL,没找到再用MODULE模式查找,这样就能确保找到CURL::libcurl target可以在项目正常连接。
CURLTarget.cmake
也许你会想问为什么CONFIG
模式能正确找到静态编译的CURL的依赖库?
那么请看看你在第一步编译好的CURL生成的cmake脚本:
curl-vc140-x64\lib\cmake\CURL\CURLTargets.cmake 1
如下是CURLTargets.cmake 中的代码片段
# Create imported target CURL::libcurl
add_library(CURL::libcurl STATIC IMPORTED)
set_target_properties(CURL::libcurl PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB"
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
INTERFACE_LINK_LIBRARIES "wldap32;winmm;ws2_32;OpenSSL::SSL;OpenSSL::Crypto;ZLIB::ZLIB;advapi32;crypt32"
)
可以看到在CURLTargets.cmake创建了名为CURL::libcurl
的 import target ,并设置了CURL::libcurl
的INTERFACE_LINK_LIBRARIES
属性为"wldap32;winmm;ws2_32;OpenSSL::SSL;OpenSSL::Crypto;ZLIB::ZLIB;advapi32;crypt32"
,
这就是定义了CURL::libcurl
的所有依赖库。
并且还通过INTERFACE_COMPILE_DEFINITIONS
属性定义了宏CURL_STATICLIB
以告诉引用CURL::libcurl
的项目使用的是CURL静态库。
那么为什么MODULE
模式不能正确找到静态编译的CURL的依赖库?
而cmake内置MODULE方式查找CURL的脚本(cmake-3.17.1-win64-x64/share/cmake-3.17/Modules/FindCURL.cmake)2是按搜索CURL动态库的方式来实现的,在创建CURL::libcurl
时并没有设置INTERFACE_LINK_LIBRARIES ,INTERFACE_COMPILE_DEFINITIONS
属性
只是按常规设置了INTERFACE_INCLUDE_DIRECTORIES
(include文件夹位置),IMPORTED_LOCATION_RELEASE
(库文件位置)属性
对于CURL动态库,这种方式没有问题,但对于编译的CURL静态库,由于CURL::libcurl
imported target不能提供完整的依赖库信息,就会导致在项目编译没问题,但连接的时候会报错找不到符号。