前言

上一篇文章讲解了如何通过 Conan 打包 Drogon 依赖的 Trantor 库。这篇文章继续总结 Drogon 的打包过程,以及将 Drogon 制作成 Conan 包时可能遇到的问题。

如果你还没有阅读上一篇文章,建议先查看 《使用 Conan 打包第三方库:Trantor 篇》 。上一篇文章已经将 Drogon 的依赖库 Trantor 制作成了 Conan 包,本文将在此基础上继续完成 Drogon 的打包。

1. 创建包

使用以下命令创建 Drogon 1.3.0Conan 包模板:

        
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. 添加依赖

首先从 DrogonGitHub 仓库中获取基础信息,并完善 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 需要生成 GUIDWindows 系统已经提供了相应能力,而 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 配置

DrogonCMakeLists.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 官方文档。官方文档通常更加权威,也能提供更完整的配置说明。