前言

昨天总结了一下conan的使用 《C++包管理器Conan使用笔记》 ,今天打算将conan打包的过程再总结一下,因为conan-center也并不是所有的第三方库都有,有些我们自己的三方库或者开源的第三方库并没有提交到conan-center,这是个时候就需要我们自己来打包,整个过程不算特别复杂,你需要具备一些python基础语法,不需要太多,能看懂基本语法就行。因为conan是基于python开发的包管理器,所以打包的时候需要使用pyhon去修改构建过程。这个例子我选择的是一款国产的C++ Web开发框架 Drogon ,这是一款非常优秀的Web开发框架。Here we go !

准备工作

在开始之前,你的系统必须准备conancmakegit编译器,这几个软件才能正常运行下面的例子。也可以参照官方文档,毕竟官方文档的解释是权威的,下面的很多知识在官方文档都有体现和解释: https://docs.conan.io/en/latest/creating_packages.html

查看依赖

首先我们要做的事情是查看Drogon有没有依赖其他库,我们从DrogonWIKI可以看到,Drogon依赖了trantorjsoncpplibuuidzlibOpenSSL(非必须)c-ares(非必须)libbrotli(非必须)postgreSQLmariadbsqlite3这些第三方库,他的依赖关系图如下所示:

Drogon依赖图
Drogon依赖图

可以看到这应该算得上比较复杂的依赖关系了,我们应该注意红色的部分,trantordrogon这两部分是需要我们自己管理的两个库,trantor也是drogon作者开发用于drogon的网络和工具的库,所以我们需要先将trantor进行打包上传。trantor在这里,通过上图分析可以看见,trantor也有他自己的依赖,OpenSSLc-ares。我们上conan-center搜索可以发现,这两个库在conan-center已经有了,所以可以直接进行引用。

创建trantor包

我们先通过conan new命令创建trantor的包:

        
shell
12
mkdir trantor && cd trantor conan new trantor/1.2.0 -t

包名称应该以 <包名>/<版本号>[@作者/通道] 格式进行管理,其中作者和通道可以忽略,-t代表我们需要测试文件,运行以上代码我们将得到如下文件:

        
text
12345
conanfile.py test_package CMakeLists.txt conanfile.py example.cpp

其中conanfile.py就是我们的包管理描述和构建文件,它里面带有包的描述和构建规则,test_package文件夹里面是测试文件,可以理解成消费者,包生成成功之后会使用test_package去引用包并调用里面的测试代码,用于检查打包是否成功。由于篇幅原因就不贴代码上来了!

我们先将包的基本信息修改,licenseauthorurldescriptiontopics可以去trantorgithub上复制下来, https://github.com/an-tao/trantor

对于这个文件代码的详细描述,我建议去看一下官方文档,说得比较详细: https://docs.conan.io/en/latest/creating_packages/getting_started.html

不过我也依然会尝试性的去解释每一个方法代表什么意思,如果你有python基础, 那么不难看出这里面有五个方法,他们代表编译打包的五个阶段,其他比如name、version等可以理解成属性。

1.config_options你可以理解成在编译前需要做的一些准备,比如针对平台的一些配置之类的。

2.source 这里代表对源代码的操作,比如下载源代码,修改构建规则

3.build 构建和编译的事情在这里

4.package打包的代码在这里,理解成将需要的文件拷贝到包里面准备上传,消费者将使用这些文件

5.package_info 打包的一些信息。

包的基础信息填写好了之后我们可以开始了。

添加依赖和构建系统

上面我们分析过trantor也有两个依赖,我们需要将依赖添加进去,否则编译的时候会报错,依赖的添加方式和使用的时候是同一个字段,只需要在generators字段下面添加requires字段:

        
text
1
requires = ["openssl/1.1.1i", "c-ares/1.17.1"]

这样依赖就被添加进去了,继续添加构建系统,通过查看trantorCMakeLists.txt文件可以发现,它是通过find_package函数来查找依赖的,并且使用的是cmake提供的路径,所以我们需要在构建字段添加cmake_find_packagecmake_paths这两个值。如下:

        
text
1
generators = "cmake", "cmake_find_package", "cmake_paths"

拉取源码和修改构建规则

在默认生成的文件里面,source(self)方法有一段运行git的代码

        
Python
1
self.run("git clone https://github.com/conan-io/hello.git")

这是使用git下载源代码,他其实是就是在本机执行一段shell命令,你可以改成其他的。我们将地址改成trantor的地址:

        
Python
1
self.run("git clone https://github.com/an-tao/trantor")

接下来,我们需要注入conan到目标的CMakeLists.txt里面,使得它可以使用conan来引用依赖,我们需要用到conan给我们提供的工具函数tools.replace_in_file()他有三个参数,第一个参数是目标文件,第二个是查找匹配的字符串,第三个是需要替换的字符串。比如:tools.replace_in_file("test.txt", "hello", '''Hello World !'''),这个例子会将test.txt文件里面的hello替换成Hello World !,需要注意的是第三个参数并不是引号包裹而是六个高撇号包裹起来的,我们将生成的默认tools.replace_in_file函数修改成:

        
Python
123456
tools.replace_in_file("trantor/CMakeLists.txt", "project(trantor)", '''project(trantor) 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})''')

这样,在构建的时候trantor会使用conan提供的依赖环境去生成库文件。

配置构建选项

通常,我们是使用库的时候有动态和静态链接库的区别,编译动态和静态链接库我们在build函数里去设置,我们查看tratorCMakeLists.txt文件可以看到有一个BUILD_TRANTOR_SHARED开关来控制编译动态还是静态库,默认是静态链接库,我们在cmake = CMake(self)下面使用definitionscmake传递参数如下:

        
Python
123
if self.options.shared: cmake.definitions["BUILD_TRANTOR_SHARED"] = "On" pass

这样我们就可以通过conanshared变量来控制编译的是静态还是动态库,还记得shared吗?我们在安装的时候执行的命令:conan install .. -o *:shared=True说的就是这里了。可以看一下我的另一篇拙作 《C++包管理器Conan使用笔记》 ,记得将下面的source_folder改成trantor

打包库文件

完成上面这个繁杂的步骤,接下来我们就准备打包库文件了,我们在使用一个库的时候通常需要哪些文件呢?include文件,.lib或者.a文件,.dll或者.so文件,这些都是必须的,所以我们到package函数里面修改一下默认的代码,不过在这之前,我需要尝试解释一下默认代码里面的另一个工具函数self.copy,通过看函数名应该知道,这其实就是一个复制文件的函数,我不太清楚python管这种函数(或者这不是函数?)参数叫啥,我就暂且叫他参数名(错了也不要骂我)?这个函数有四个参数,除了第一个另外三个应该是可选项,第一个参数就是需要拷贝的文件名,它支持通配符。

dst 字段表示需要拷贝到的目标文件夹。

src 表示文件来源的目录。

keep_path 表示是否需要保持文件路径,比如:如果设置为True,文件在src/Debug/lib/拷贝的目标目录为lib那么他的路径为lib/src/Debug/lib/,这个需要自行去实践一下。

还有一点值得一提,如果没有指定src将会递归搜索整个项目目录直到找到然后拷贝到目标目录。我这里将keep_path全部使用默认的False

trantor的目录结构其实算比较简单的,所以这里的改动并不大,只需要把默认的hello改成trantor就好,当然,还有一个细节问题需要我们修改一下,默认生成的代码里面.so文件是拷贝到lib目录里面的,这在用户使用的时候会出现找不到动态链接库,所以需要将lib修改成bin。最后一个是package_info这个我了解得不多,不知道实际有什么意义,暂且将默认的hello改成trantor吧。

测试包

至此,我们对包的conanfile.py的修改就结束了,接下来还需要修改测试例子,因为打包结束之后conan会自动编译运行测试例子以保证上面打的包可用。

打开test_package目录,文件CMakeLists.txtconanfile.py在生成的时候conan已经帮我们把依赖引用的代码写好了,这里的conanfile.py和上文修改的并不一样,这个是消费者使用的文件,可以看一下我的另一篇拙作 《C++包管理器Conan使用笔记》 ,虽然没提到用conanfile.py但是作用和conanfile.txt作用一致。

我们需要改的是example.cpp文件,它里面生成了一些非常简单的代码,但是这并不能验证trantor可用,复制下面的代码:

        
cpp
123456789
#include <iostream> #include <trantor/utils/Date.h> using namespace trantor; int main(int argc, char* argv[]) { std::cout << "Trantor Now is: " << Date::now().toFormattedString(true) << std::endl; return 0; }

这段代码会使用trantor里面的方法返回当前的系统时间并输出到控制台。接下来就交给conan了。

创建并测试包

下面,我们将使用conan create命令生成trantor库文件,在项目根目录执行命令:

        
shell
1
conan create .

这里说一下,conan create .命令其实是conan export .conan install trantor/1.2.0 --build=trantorconan test test_package trantor/1.2.0指令的集合,你可以分开执行。

我们在创建的时候可以传递一些设置,像conan install命令一样,比如:conan create . -o trantor:shared=True

还记得第五小节的配置吗?这个命令就能让第五小节的设置生效,或者传递-s build_type=Debug区分开发包和发行包, 如果你看到如下输出:

        
text
12
trantor/1.2.0 (test package): Running test() Trantor Now is: 20210303 09:12:10.254014

那么,打包就算大功告成了。我们使用就能像其他包一样正常使用了。当然,现在只是在我们本机使用,conan支持自建私服我们可以将包通过conan upload上传到我们私服,这点我后面会继续写一篇相关的文章。

本例子的项目已上传GitHub: https://github.com/guosuying2012/trantor-conan

总结

总的来说trantor的配置还是比较简单的,里面的弯弯绕并不是特别多,这也给了我继续下去的信心,毕竟刚开始,需要循序渐进的去了解conan打包的一些知识,配合阅读官方文档,整个过程算是比较轻松,只是过程中难免踩到一些坑,但是官方文档上面有非常详细的解释,所以也能很好的应对,至少这是一个很好的开头。

热爱生活,热爱编程,加油!!

2021年3月3日 农历正月二十