基于 Conan 的 C/C++ 持续交付流水线_c/c++ ci工具-程序员宅基地

技术标签: C/C++  DevOps  JFrog  Artifactory  Conan  

背景

在当下软件应用的开发过程当中,单枪匹马或者小作坊式的模式已经很少见了,协作式的开发成为主流。相应的,应用的代码也不再是从零开始,而是基于或引用很多已有的、共享的模块,如各种开源的框架和共用库,或者协作团队中开发的自研库,这就是软件开发中常说的“依赖”。为了更好地管理这种依赖关系,各种开发语言都逐渐发展出了自己的依赖管理系统,如 Java 的 Maven、NodeJS 的 NPM、Python 的 Pypi 等。这些依赖管理系统的日渐完善和广泛应用,使得相应语言的应用开发更加简洁、高效,大大推动了软件应用的发展和普及。

然而相对的,作为软件开发重要组成部分的 C/C++ 语言,由于其编译型语言的特性,即应用最终要编译成为目标机器可直接执行的程序,使得 C/C++ 的依赖管理一直是一个众所周知的难题和痛点。这主要体现在:

  • 应用二进制接口不兼容。C/C++ 的依赖不仅仅体现在代码上,还包括操作系统、架构、编译器等环境配置上,为了确保一个共享库与其他库、整个应用的兼容性,必须通过各种配置来描述这些配置的依赖信息。

  • 编译构建慢。由于头文件和预处理机制,以及上面提到的兼容性,需要额外的机制来提升编译效率,才能保证只编译那些需要重新编译的代码。

  • 代码链接和内嵌。一个静态的 C/C++ 库能够被另一个库通过头文件包含的方式引用,而一个共享库也能嵌入另一个静态库。在两种情形中,当任何依赖变更时,都必须管理哪些库是需要重新构建的。

  • 生态系统的快速发展。针对不同平台、不同构建任务及应用场景的编译器、构建系统层出不穷,导致解决上述问题的工作量不断地增加。

当然,针对 C/C++ 的依赖管理,业界也开展了很多研究和实践工作,可惜大多数的效果并不理想。而本文将会介绍一种当前已逐步得到业界关注和认可,并得到大力推广的 C/C++ 依赖管理方案--Conan,以及如何基于 Conan、持续集成(CI)工具 Jenkins 和全语言制品库 Artifactory,实现 C/C++ 应用的持续交付流水线。

 

Conan--C/C++ 的包管理器

Conan 是一个开源项目(FOSS,Free Open Source Software),https://conan.io ,为 C/C++ 的依赖包管理构建了非中心化的管理架构,开发者可以像 Docker Registry 一样搭建自己专属的依赖包仓库。Conan 是跨平台的解决方案,同时兼容各种构建工具,以依赖包的二进制文件为基础来定义、管理依赖关系,使得依赖包的获取和消费更加符合目标环境和配置的特点,提升了整体编译的效率。Conan 是基于 Python 语言的,上手门槛较低,也易于扩展。在被 JFrog 收购之后,Conan 在保持开源特性的同时,还结合了 JFrog 产品在制品管理、DevOps 工具集成等方面的能力,为开发者提供了更为全面的支持。Conan 在当前 C/C++ 依赖管理领域已得到广泛关注和快速发展。

通常之前介绍的像 Maven 这样的依赖管理系统,都包含以下几个部分:

  • 依赖包仓库,用以存储需要引用的依赖包,即各种通用框架或共享模块;

  • 依赖访问协议,用以描述如何在依赖包仓库中定位、获取或上传共享模块;

  • 依赖描述语言,用以描述如何定义依赖关系,以及后台对依赖关系的自动解析;

  • 客户端,根据描述语言中的定义,遵照访问协议,从仓库中获取或上传相应的依赖包。

Conan 也在这几方面提供了成熟的解决方案:

 

  • 依赖包仓库

Conan 在 JFrog 的公网制品分发平台 JFrog Bintray 上搭建了公共的依赖包仓库conan-center(https://conan.bintray.com),开发者可以直接在这里获取所需的各种公共依赖包。

同时,开发者还可以在本地搭建原生的 conan_server,或者直接利用 JFrog Artifactory 制品仓库,做为私有的依赖包仓库。

 

  • 依赖访问协议

Conan 通过“<包名>/<版本号>@<所有者>/<成熟度>”的命名规则来定位一个依赖包,其中“<所有者>/<成熟度>”(user/channel)定义了一个类似于命名空间(NameSpace)的机制,用于区分针对同一个共享库的不同实现。

每一个依赖包都分为 recipe 和 package 两个部分。 recipe 定义了依赖包的基本信息、依赖关系、构建方法等基本信息,package 则根据目标环境和配置,如操作系统、架构、编译器等(即 Conan 中的 setting)的不同,保存对应的二进制实现。这样,客户端访问时,先根据命名规则定位到 recipe,再根据目标 setting 的不同选择对应的二进制 package 来下载、使用。

在 conan-center 中,各种公共库都根据目标 setting 的不同提供了大量的二进制 package 供开发者直接使用,大大提高了 C/C++ 应用的编译效率。

 

  • 依赖描述语言

Conan提供了简单明了的依赖关系描述方式,在后续的示例中会做详细解读。

 

  • 客户端

Conan 的客户端提供了丰富的命令行命令,能够方便地实现依赖关系的解析和依赖包的管理。

Conan 客户端的安装也非常简便,大家可以参考文档自己实践(https://docs.conan.io/en/latest/installation.html)。

安装好之后,我们可以运行第一个命令:

可以看到 Conan 客户端已经预先配置好了与公共库 conan-center 的连接,开发者可以直接使用其中的公共依赖包。

本文后续将通过示例来展示 Conan 如何利用这些解决方案来提供服务。

 

Conan 应用示例

本文将基于一个简单的 C++ 应用来展示如何使用 Conan,其代码可以在https://github.com/xingao0803/demo-poco-timer.git  中获得,供大家参考。

 

  • 描述依赖关系

示例中的 C++ 应用,timer.cpp,是一个简单的 timer 程序,引用了公共库 POCO:

为了在 Conan 中描述与 POCO 的依赖关系,需要编写 conanfile.txt,这就是上一节提到的依赖描述语言。

其中,[requires] 部分列出了本应用需要的依赖包,这里是 POCO,1.8.0.1版本,而且是由 pocoproject 提供的稳定(stable)版本。

[generators] 则列出了编译本应用使用的编译器类型。Conan 提供的公开示例大多是基于 cmake 的,这里改用更为通用的 compiler_args,不限定编译器的类型。

当然,这里只是一个简单的例子,conanfile.txt 的更多内容请参考 Conan 官方文档https://docs.conan.io/en/latest/reference/conanfile_txt.html。

我们知道,在软件开发中,除了代码中的直接依赖,还会有各种传递依赖。而 conanfile.txt 里只列出了直接依赖。Conan 的客户端提供了“conan info”命令来解析所有的依赖关系。

在 conanfile.txt 所在目录执行:

本应用相关的各种依赖传递关系就会在 denpendencies.html 里展示出来,如下图:

此时再执行:

可以看到,相关依赖包的 recipe 已经下载到本地 cache 里了。

 

  • 下载依赖包二进制文件

“conan info” 命令只是下载了依赖包的 recipe。要下载对应 setting 的 package 二进制包,用以编译,还需要执行 “conan install” 命令。

在 conanfile.txt 所在目录创建构建子目录,并执行 “conan install”:

从运行结果可以看出,Conan 的客户端根据本地的 setting 设置和依赖包的 recipe,自动从 conan-center 上获取对应的二进制 package,下载到本地 cache 里。

当然,由于 C/C++ 生态系统的快速发展,Conan 现有的二进制 package 不一定能够覆盖所有的 setting 组合。“—build=missing” 参数就是指定在没有 setting 对应的二进制 package 时,根据 recipe 中定义的方法,自动从依赖包的源代码编译出对应的二进制包,存储在本地 cache 中。例如:当我在 macbook 上执行上述操作时,POCO 就需要进行重新编译:

当我们再次运行 “conan install” 命令时可以发现,此时相关的依赖包已经可以直接从本地 cache 获取了,避免了重复的网络访问和编译工作。

 

  • 上传到私有依赖包仓库

在当前团队协作开发的模式下,仅仅把依赖包下载到本地 cache 是远远不够的,我们还需要让整个团队都能够分享这些依赖包。这样即避免了重复的网络访问和编译,又保证了团队中依赖引用的一致性。此时就需要引入私有的依赖包仓库。

Conan 提供了原生的 conan_server 作为本地化部署的私有仓库,可以参考文档https://docs.conan.io/en/latest/uploading_packages/running_your_server.html。

而这里我们要推荐 JFrog 的 Artifactory 全语言制品仓库。Artifactory 不仅仅可以做为 Conan 仓库,其全语言的支持能力还使其能够同时提供 Maven、NPM、Docker 等依赖仓库的服务。 Artifactory 还提供元数据的能力,也就是可以在仓库存储制品的属性上记录整个 DevOps 过程中的关键数据。此外,Artifactory 还可以和 Jenkins 紧密集成,在 Jenkins Pipeline 中提供针对各种开发语言的 DSL,方便持续交付流水线的开发与编排。Artifactory 全语言、元数据,以及集成 Jenkins 的优势在后续示例中都能够得到体现。

Artifactory 的相关信息可以参见文档https://www.jfrog.com/confluence/display/RTF/Welcome+to+Artifactory ,“JFrog 杰蛙 DevOps” 微信公众号上也有很多相关文章和视频课程供大家参考。此外,JFrog Artifactory 还针对 Conan 的应用推出了社区版-- Artifactory CE,https://docs.conan.io/en/latest/uploading_packages/artifactory_ce.html,供大家使用。

为了使用 Artifactory 建设私有的 Conan 依赖包仓库,我们需要在 Artifactory 创建一个 Conan 类型的 local repository。创建方法参见https://www.jfrog.com/confluence/display/RTF/Conan+Repositories 。

然后,我们需要执行 Conan 的客户端命令 “conan remote add” 和 “conna user” 加入这个私有仓库。具体的执行方法在 Artifactory Repository 的  “Set Me Up” 部分有清晰的描述:

其中 <REMOTE> 作为这个私有仓库的别名,会在后续的命令中用来指定使用该仓库。

私用仓库创建好之后,我们可以使用 “conan upload” 命令来上传下载到本地 cache 的依赖包:

其中 -r <REMOTE> 用以指定目标私有仓库,--all 指定同时上传 recipe 和 package。

运行之后,在 Artifactory 的 Conan repository 里就可以看到上传的依赖包了。

之后当再次需要使用这些依赖包时,就可以在执行 “conan install” 时利用 “-r <REMOTE>” 参数指定从私有仓库获取了。

 

  • 编译C++应用

执行 “conan install” 之后,除了会下载相应依赖包的二进制 package 之外,还会自动生成编译相关的参数引用文档。如在前面的示例中,build 目录下会自动生成 conanbuildinfo.args 文件,其中包含了编译过程中如何引用相应依赖包的参数设置。当编译时,可以直接引用这些参数:

可以看出,基于 Conan 的依赖管理,只要通过 conanfile.txt 描述依赖关系,通过 conan install 命令下载依赖包 package,我们就可以便捷地完成 C++ 应用的编译。

 

  • 管理C++应用

当然,从应用开发过程来看,仅仅完成编译还是不够的,我们还需要管理好编译产出的可执行程序,供后续测试、部署、发布等环节使用。

通常会把应用编译好的可执行程序存储到代码管理系统,如 git 或 svn 中,或者文件服务器,如 ftp 上。但这种方式会丢失掉应用程序特定的 setting 信息,而且会对现有系统造成性能上的影响。

这里我们还是推荐使用 Artifactory,利用其 Generic repository 来存储可执行程序。Artifactory 全语言的支持使得能够在同一个仓库中统一管理 Conan 依赖包和编译好的可执行程序。而其企业级高可用的特性能够保证制品访问的性能和稳定。

在 Artifactory 里创建一个 Generic 的 local repository,在其 “Set Me Up” 部分可以看到如何利用 Artifactory 的 rest api 来上传和下载相应的可执行程序:

同时,我们可以在 repository 里通过设置不同的目录来记录可执行程序的版本。

针对可执行程序特定的 setting,也可以通过Artifactoy 的元数据能力记录到对应程序的属性上。

在执行完 “conan install” 之后,build 目录里还会自动生成 conaninfo.txt 文件,记录了本应用及其依赖相关的各种配置参数,其中 [settings]、[full_settings] 部分记录了相关的 setting 信息:

可以将这些 setting 参数提取出来,利用 Artifactory 的 rest api 写入属性:

在 Artifactory 的 repository 里就可以看到这些属性了:

此外,Artifactory 还提供了 AQL(Artifactory Query Language),使得我们能够基于这些元数据属性设置查询条件,从而定位与目标 setting 匹配的可执行程序。

设置 aql 文件 1timer.aql 如下:

再利用 Artifactory 的查询 rest api,我们就可以定位和下载符合 setting 需求的可执行程序,供后续的测试、部署等环节使用了。

同时,我们还可以利用 Artifactory 元数据的能力,将后续环节的关键数据,如测试结果,也记录在该程序的属性中,从而为质量监控审批、出错回溯检查等提供数据支持。

 

C/C++持续交付流水线

前面的示例,我们利用 Conan 依赖管理和 Artifactory 私有仓库实现了完整的 C++ 应用开发流程。在当前 DevOps 应用背景下,我们还需要能够自动化地重复执行这一流程,以实现 C++ 应用的持续交付。这就需要利用 Conan、Artifactory 和 Jenkins 这一类工具的集成来实现。

Artifactory 提供了 Jenkins 的插件,支持 Jenkins Pipeline 中针对 Artifactory 和 Conan 的 DSL,可以很方便、直观的实现 Artifactory 和 Conan 的各种操作。前面示例中的各种操作,都可以利用相应的 DSL 语句,集成到 Jenkins Pipeline 当中,如:

完整的 Jenkins Pipeline 代码在https://github.com/xingao0803/demo-poco-timer.git  中可以找到,供大家参考。

在 Jenkins 中执行该 Pipeline,就可以实现 timer 应用的自动化持续交付流水线:

 

总结

C/C++ 的依赖管理一直是软件开发领域的痛点。本文介绍了当前得到广泛关注和迅速发展的 C/C++ 依赖管理解决方案--Conan 的基本原理和应用流程。同时,利用 timer 应用示例,展示了如何基于 Conan、Artifactory 和 Jenkins,实现C/C++应用的持续交付流水线。

当然,本文中的示例只展示了 Conan 最基本的应用场景,大家可以根据相关资料,学习和研究 Conan 更多的案例。我们也会陆续推出后续文章,为大家进一步展示 Conan 的特性和应用方式。

 

文章作者:高欣,JFrog中国高级架构师

 

参考文献

  • Conan 官网https://conan.io

  • JFrog 官网 http://www.jfrogchina.com

  • Artifactory 文档 https://www.jfrog.com/confluence/display/RTF/Welcome+to+Artifactory

  • Artifactory社区版 https://docs.conan.io/en/latest/uploading_packages/artifactory_ce.html

  • 示例代码 https://github.com/xingao0803/demo-poco-timer.git

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wangqingjiewa/article/details/86476231

智能推荐

java socket与io流_Java socket中关闭IO流后,发生什么事?(以关闭输出流为例)-程序员宅基地

文章浏览阅读108次。声明:该博文以socket中,关闭输出流为例进行说明。为了方便讲解,我们把DataOutputstream dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));中的dout做为Socket输出流的代言。同样的,din是输入流的代言。可以造成dout被关闭的操作有:1、调用dout.clo..._error:java.net.socketexception: socket output is shutdown

YOLOV4 SPP模块_yolo中使用spp的目的是什么-程序员宅基地

文章浏览阅读4.2k次,点赞2次,收藏8次。SPP模块首要作用是用来解决输入图像尺寸不统一的问题,SPP中不同大小特征的融合,有利于待检测图像中目标大小差异较大的情况,尤其是对于yolov3一般针对的复杂多目标图像。代码实现:_yolo中使用spp的目的是什么

Kafka基础-原理、运维与开发_kafka、zookeeper运维-程序员宅基地

文章浏览阅读1k次。文章目录1.kafka基本原理1.1 消息系统的作用1.2 kafka核心概念1.3 kafka集群架构1.4 kafka高性能高可用原理2.kafka运维实战2.1 kafka生产集群部署方案2.2 kafka常用命2.3 kafka监控-Kafkamanager3.kafka开发实战3.1 kafka生产者3.1.1 生产者发消息原理3.1.2 生产者demo代码3.1.3 生产者核心参数3.2 kafka消费者3.2.1 消费者消费几个重要概念3.2.2 消费者demo代码3.2.3 消费者重要参数_kafka、zookeeper运维

Yii2.0框架学习笔记_yii后台 记录哪张表哪条数据-程序员宅基地

文章浏览阅读432次。1、Yii框架工作流程(MVC文件后缀都是php):models文件夹下建模型,写AR类、对应数据库相关表、定义属性标签、写验证规则、保存表之前调用的方式,用于保存非表单获取的默认数据(如添加数据的时间等,此步如不需要可省略)->controllers文件夹下建控制器、写保存表单的动作->views文件夹下建模板,动态显示视图。2、YII中各种类模型中添加:use yii\ba..._yii后台 记录哪张表哪条数据

NLP实战之BERTopic主题分析-程序员宅基地

文章浏览阅读9.3k次,点赞26次,收藏140次。BERTopic是一种结合了预训练模型BERT和主题建模的强大工具。它允许我们将大规模文本数据集中的文档映射到主题空间,并自动识别潜在的主题。它背后的核心思想是通过BERT模型来捕获文档的语义信息,并然后使用主题建模技术来对这些语义信息进行聚类,从而得出主题。对于大部分小伙伴而言,知道如何去使用BERTopic模型进行主题分析就足够了。所以BERTopic原理就不做过多解释了,感兴趣的小伙伴可以自己去了解一下。BERTopic是一种强大的主题分析工具,它能够自动识别文本数据中的主题,而无需预先定义主题数。_bertopic

idea运行tomcat过程中 中文变����_tomcat 运行-程序员宅基地

文章浏览阅读2.6k次。**需要配置一下tomcat编码**-Dfile.encoding=UTF-8_tomcat 运行

随便推点

外观模式实例-智能手机一键备份_某软件公司为新开发的智能手机控制与管理软件提供了一键备份功能,通过该功能可以-程序员宅基地

文章浏览阅读2.2k次,点赞7次,收藏40次。外观模式实例问题描述结构图编程实现需要交互的类Facade类客户端问题描述某软件公司为新开发的智能手机控制与管理软件提供一键备份功能,通过该功能可以将原本存储在手机中的通讯录、短信、照片、音乐等资料一次性拷贝到移动存储介质中(例如:SD卡)中。在实现过程中需要与多个已有的类进行交互,如通讯录管理类、短信管理类。结构图编程实现需要交互的类public class ContactsManager { private String contacts; public Contacts_某软件公司为新开发的智能手机控制与管理软件提供了一键备份功能,通过该功能可以

走出MFC子类化的迷宫_vs怎么用mfc画出迷宫-程序员宅基地

文章浏览阅读1k次。本文转自:http://www.99inf.net/SoftwareDev/VC/22322.htmWindows给我们或是说给它自己定义了许多丰富的通用控件,如:Edit、ComboBox 、ListBox……等,这些控件功能丰富,能为我们开发工作带来极大方面,试想:我们单单是自己实现一个EDIT控件是多么的艰难!但是,在实际开发中还是有些情况这些标准控件也无能为力,比如:在我们的应用中要求_vs怎么用mfc画出迷宫

【矩阵学习】Jacobian矩阵和Hessian矩阵_海森矩阵判断是否为驻点-程序员宅基地

文章浏览阅读6.4k次,点赞5次,收藏42次。【矩阵学习】Jacobian矩阵和Hessian矩阵Jacobian 矩阵Jacobian 行列式功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchar..._海森矩阵判断是否为驻点

回顾HTTP协议和报文_协议报文和数据报文的区别-程序员宅基地

文章浏览阅读487次。计算机网络这门课是在大二上的时候是作为主修课,当然上这门课的时候有点划水了。后来接触到校招这方面,下定决心恶补计算机网络这方面的知识,就买了一本《计算机网络:自顶向下的方法》研习。如果不想读谢希仁那版书(毕竟晦涩难懂),又想比较系统的了解计算机网络这方面知识,这本书真的非常推荐。划重点:简单回顾一下HTTP及其报文。1、什么是HTTP协议?HTTP 超文本传输协议 (HTTP-Hypertext transfer protocol),是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用_协议报文和数据报文的区别

Flash / Flex Tutorial - How to Create a crossdomain.xml file-程序员宅基地

文章浏览阅读535次。Flash / Flex Tutorial - How to Create a crossdomain.xml file.This brief tutorial will teach you how to create a crossdomain.xml file so that you can access files and information from outside domains

Redis Sentinel机制与用法[转]_sentinel: redis://10.43.19.251:26379 频繁 added down-程序员宅基地

文章浏览阅读1.3k次。Redis Sentinel 机制与用法(一)概述Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动_sentinel: redis://10.43.19.251:26379 频繁 added down

推荐文章

热门文章

相关标签