使用 Conan 打包第三方库:Drogon 篇
前言
上一篇文章讲解了如何通过 Conan 打包 Drogon 依赖的 Trantor 库。这篇文章继续总结 Drogon 的打包过程,以及将 Drogon 制作成 Conan 包时可能遇到的问题。
如果你还没有阅读上一篇文章,建议先查看 《使用 Conan 打包第三方库:Trantor 篇》 。上一篇文章已经将 Drogon 的依赖库 Trantor 制作成了 Conan 包,本文将在此基础上继续完成 Drogon 的打包。
1. 创建包
使用以下命令创建 Drogon 1.3.0 的 Conan 包模板:
bash
12
mkdir drogon && cd drogon
conan new drogon/1.3.0 -t
执行后会生成如下文件结构:
text
12345
conanfile.py
test_package/
├── CMakeLists.txt
├── conanfile.py
└── example.cpp
这一部分与 Trantor 包的创建过程基本一致。
2. 添加依赖
首先从 Drogon 的 GitHub 仓库中获取基础信息,并完善 conanfile.py。
构建生成器和依赖配置如下:
Python
123456789101112
generators = "cmake", "cmake_find_package", "cmake_paths"
requires = [
"openssl/1.1.1i",
"trantor/1.2.0",
"jsoncpp/1.9.4",
"zlib/1.2.11",
"brotli/1.0.9",
"libpq/13.1",
"mariadb-connector-c/3.1.11",
"sqlite3/3.34.0",
]
可以看到,上一篇文章中制作的 Trantor 包已经能够作为 Drogon 的依赖使用。
2.1 根据平台添加 libuuid
Drogon 需要生成 GUID。Windows 系统已经提供了相应能力,而 Linux 平台还需要依赖第三方库 libuuid。
因此,需要通过 requirements() 方法判断当前平台,并在非 Windows 平台下添加 libuuid:
Python
123
def requirements(self):
if self.settings.os != "Windows":
self.requires("libuuid/1.0.3")
Conan 官方文档:
3. 拉取源代码并修改构建规则
3.1 拉取 Drogon 源代码
将 self.run() 中的仓库地址修改为 Drogon:
Python
1
self.run("git clone https://github.com/an-tao/drogon.git")
3.2 向 CMake 注入 Conan 配置
在 Drogon 的 CMakeLists.txt 中注入 Conan 生成的构建信息:
Python
123456789
tools.replace_in_file(
"drogon/CMakeLists.txt",
"project(drogon)",
"""project(drogon)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
include(${CMAKE_BINARY_DIR}/conan_paths.cmake)
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR} ${CMAKE_MODULE_PATH})"""
)
3.3 禁用 Drogon 内置的 Trantor 子目录
Drogon 默认通过 CMake 的 add_subdirectory() 引入 Trantor。由于我们已经将 Trantor 制作成独立的 Conan 包,因此可以注释掉这部分构建逻辑:
Python
1234567891011
tools.replace_in_file(
"drogon/CMakeLists.txt",
"add_subdirectory(trantor)",
"# add_subdirectory(trantor)"
)
tools.replace_in_file(
"drogon/examples/CMakeLists.txt",
"${PROJECT_SOURCE_DIR}/trantor/trantor/tests/server.pem",
""
)
第二处修改用于取消示例程序中对 server.pem 文件的复制。
3.4 处理 Windows 下的 Rpcrt4 链接
Windows 不需要 libuuid,但生成 GUID 时仍然需要链接系统库 Rpcrt4。
因此,需要根据平台修改 Drogon 中的 link_libraries() 和 target_link_libraries():
Python
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
if self.settings.os != "Windows":
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PUBLIC trantor)",
"target_link_libraries(${PROJECT_NAME} PUBLIC trantor ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE dl)",
"target_link_libraries(${PROJECT_NAME} PRIVATE dl ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi)",
"target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)",
"target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PUBLIC Jsoncpp_lib)",
"target_link_libraries(${PROJECT_NAME} PUBLIC Jsoncpp_lib ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE UUID_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE UUID_lib ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE Brotli_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE Brotli_lib ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE MySQL_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE MySQL_lib ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE SQLite3_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE SQLite3_lib ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB)",
"target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)",
"target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto ${CONAN_LIBS})"
)
tools.replace_in_file(
"drogon/examples/CMakeLists.txt",
"link_libraries(${PROJECT_NAME})",
"link_libraries(${PROJECT_NAME} ${CONAN_LIBS})"
)
else:
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PUBLIC trantor)",
"target_link_libraries(${PROJECT_NAME} PUBLIC trantor ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE dl)",
"target_link_libraries(${PROJECT_NAME} PRIVATE dl ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi)",
"target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)",
"target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PUBLIC Jsoncpp_lib)",
"target_link_libraries(${PROJECT_NAME} PUBLIC Jsoncpp_lib ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE UUID_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE UUID_lib ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE Brotli_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE Brotli_lib ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE MySQL_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE MySQL_lib ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE SQLite3_lib)",
"target_link_libraries(${PROJECT_NAME} PRIVATE SQLite3_lib ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB)",
"target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB ${CONAN_LIBS} Rpcrt4)"
)
tools.replace_in_file(
"drogon/CMakeLists.txt",
"target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)",
"target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto ${CONAN_LIBS} Rpcrt4)"
)
这里将 ${CONAN_LIBS} 注入到相关链接项中,该变量包含 Conan 依赖包提供的库文件。
3.5 修复 Brotli 静态库名称
使用静态链接库编译时,CMake 可能找不到 Brotli。原因是 Brotli 的静态库文件名带有 -static 后缀,而 Drogon 默认查找的名称没有该后缀。
可以在静态构建时修改 FindBrotli.cmake:
Python
12345678910111213141516
if not self.options.shared:
tools.replace_in_file(
"drogon/cmake_modules/FindBrotli.cmake",
"find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon)",
"find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon-static)"
)
tools.replace_in_file(
"drogon/cmake_modules/FindBrotli.cmake",
"find_library(BROTLIDEC_LIBRARY NAMES brotlidec)",
"find_library(BROTLIDEC_LIBRARY NAMES brotlidec-static)"
)
tools.replace_in_file(
"drogon/cmake_modules/FindBrotli.cmake",
"find_library(BROTLIENC_LIBRARY NAMES brotlienc)",
"find_library(BROTLIENC_LIBRARY NAMES brotlienc-static)"
)
4. 配置构建选项
Drogon 的构建逻辑与 Trantor 基本一致:
Python
12345678
def build(self):
cmake = CMake(self)
if self.options.shared:
cmake.definitions["BUILD_DROGON_SHARED"] = "On"
cmake.configure(source_folder="drogon")
cmake.build()
5. 打包库文件
Drogon 的目录结构比 Trantor 更复杂,需要分别复制头文件、库文件和可执行文件:
Python
123456789101112131415161718192021222324252627282930313233343536373839404142
def package(self):
self.copy(
"*.h",
dst="include/drogon",
src="drogon/lib/inc/drogon",
keep_path=False
)
self.copy(
"*.h",
dst="include/drogon/utils",
src="drogon/lib/inc/drogon/utils",
keep_path=False
)
self.copy(
"*.h",
dst="include/drogon/plugins",
src="drogon/lib/inc/drogon/plugins",
keep_path=False
)
self.copy(
"*.h",
dst="include/drogon/orm",
src="drogon/orm_lib/inc",
keep_path=False
)
self.copy("*.lib", dst="lib", keep_path=False)
self.copy("*.dll", dst="bin", keep_path=False)
self.copy("drogon_ctl*", dst="bin", src="bin", keep_path=False)
self.copy("dg_ctl*", dst="bin", src="bin", keep_path=False)
self.copy("webapp*", dst="bin", src="bin", keep_path=False)
self.copy("webapp_test*", dst="bin", src="bin", keep_path=False)
self.copy("client*", dst="bin", src="bin", keep_path=False)
self.copy("benchmark*", dst="bin", src="bin", keep_path=False)
self.copy("pipelining_test*", dst="bin", src="bin", keep_path=False)
self.copy("websocket_test*", dst="bin", src="bin", keep_path=False)
self.copy("multiple_ws_test*", dst="bin", src="bin", keep_path=False)
self.copy("*.so", dst="lib", keep_path=False)
self.copy("*.dylib", dst="lib", keep_path=False)
self.copy("*.a", dst="lib", keep_path=False)
记得将 package_info() 中原有的 hello 库名修改为 drogon。
6. 测试包
6.1 修改 test_package/CMakeLists.txt
将:
cmake
1
target_link_libraries(example ${CONAN_LIBS})
替换为:
cmake
12345
if (WIN32)
target_link_libraries(example ${CONAN_LIBS} Rpcrt4)
else()
target_link_libraries(example ${CONAN_LIBS})
endif()
这仍然是为了处理 Windows 下生成 GUID 所需的 Rpcrt4 系统库。
6.2 修改 test_package/example.cpp
cpp
12345678910111213141516
#include <iostream>
#include <drogon/drogon.h>
using namespace drogon;
int main(int argc, char* argv[])
{
std::cout
<< "Drogon Version: "
<< getVersion()
<< ". GitCommit: "
<< getGitCommit()
<< std::endl;
return 0;
}
7. 创建并测试包
执行以下命令创建并测试 Conan 包:
bash
1
conan create .
如果输出类似以下内容,说明包创建成功:
text
12
drogon/1.3.0 (test package): Running test()
Drogon Version: 1.3.0. GitCommit: 2919fdb7d42a38dc9f9cde181daa9a17f78b49de
示例项目:
总结
打包 Drogon 的过程比打包 Trantor 更复杂。整个过程涉及平台差异、依赖管理、CMake 构建规则修改、静态库名称适配以及测试包配置。
在实践过程中,遇到问题时应优先查阅 Conan 官方文档。官方文档通常更加权威,也能提供更完整的配置说明。
使用Conan打包第三方库「Trantor篇」
昨天总结了一下conan的使用《C++包管理器Conan使用笔记》,今天打算将conan打包的过程再总结一下,因为conan-center也并不是所有的第三方库都有,有些我们自己的三方库或者开源的第三方库并没有提交到conan-center,这是个时候就需要我们自己来打包,整个过程不算特别复杂,你需要具备一些python基础语法,不需要太多,能看懂基本语法就行...
《脚印》- 王鼎钧
你该记得那个传说:人死了,他的鬼魂要把生前留下的脚印一个一个都拣起来。为了做这件事,他的鬼魂要把生平经过的路再走一遍。车中、船中、桥上、路上、街头、巷尾,脚印永远不灭。纵然桥已坍了,船已沉了,路已翻修铺上柏油,河岸已变成水坝,一旦鬼魂重到,他的脚印自会一个一个浮上来...




