“C++ 不仅是性能最强大的语言,而且也是一门伟大的语言!它具有真正的泛型,它是表达性的,是程序员最喜欢的语言之一。”
- JFrog 软件工程师兼 Conan.io 的创建者迭戈·罗德里格斯 - 洛萨达( Diego Rodriguez-Losada )
这种表现强大的语言在所有行业中持续得到使用和青睐。然而,尽管 DevOps 在 Java 等软件开发的其他主要领域已经普遍存在,但实施现代 C 和 C ++项目的 DevOps 并不容易,尽管实际上解决方案可以提供帮助。
我们来看看 C / C ++ 开发人员面临的挑战,然后看看如何解决这些挑战。
概览
C 和 C ++ 语言由于许多重要原因而受到许多开发人员的青睐。但是,实施现代 DevOps 和持续集成 C 和 C ++ 项目并不容易。由于 C / C ++ 编程语言和生态系统的性质,它仍然具有挑战性。今天,我们有知识和经验来确定我们需要满足当今 DevOps 对 C 和 C ++ 的需求。我将用下面的案例来证明这是可能的。
开发 C 和 C ++ 项目
理想情况下,我们希望从开发人员变更到生产的 CI / CD 流程尽可能平稳和快速。但是,与其他开发语言相比,C / C ++ 开发人员面临着更大的挑战:
编译时间长:这不是因为 C 和 C ++ 编译器很慢。事实上,他们正在快速发展,在整个 IT 世界中拥有一些最先进最精彩的技术。但是,C / C ++ 有头文件的问题,预处理程序基本上在每个编译单元中一遍又一遍地复制每个包含的文件,使得编译的代码行膨胀起来。有一些机制作为预编译头文件,可以部分缓解第二次和后续重建的问题。另外,与其他语言相比,C / C ++ 语言被广泛用于大型项目,拥有数百万行代码( MLoc ) - 一个典型的 AAA 游戏有20个 MLoc 。
不同的平台和生态系统,不同的构建系统: C 和 C ++ 无处不在。它们被用于计算机,嵌入式设备,移动设备等等。但是在每个平台上,他们将使用不同的编译器(例如 Windows 中的 MSVC cl.exe ,Linux 中的 GCC,OSX 上的 apple-clang )以及许多不同的编译系统。CMake,MSBuild 和 Makefiles 或 autotools 被广泛使用,但公司也在使用其他许多构建系统,甚至开发自己的定制解决方案。
二进制兼容性: C 和 C ++ 是为本机/二进制代码构建的,但是使用某个编译器版本编译的二进制文件与其他编译器不兼容,甚至不能与不同版本的相同编译器兼容。这个问题被称为 ABI (应用程序二进制接口)不兼容性,其他语言不需要处理。ABI 不兼容可能会导致链接错误,这些链接错误在构建时很容易被捕获,但通常会产生运行时错误。
代码内嵌和嵌入:共享库直接嵌入它们链接的静态库的本地代码。但是不仅如此,即使静态库也可能嵌入从另一个库的头文件中声明的代码。这意味着可能有必要从源代码库重建二进制文件,这些文件根本就没有被修改,只是因为它的一些依赖关系已经改变了。
缺乏 OSS 的标准: OSS 库可以在 GitHub,GitLab 或者 Bitbucket 中托管他们的源代码,但是这并不能使它们准备好使用。用户需要从源代码构建二进制代码(这可能很痛苦),从系统包管理器(可能已经过时,每个平台都不相同)或者使用其他安装程序或预编译的模块(必须从一个网站)。之后,他们通常还需要对消费者构建系统进行不同的调整,这对每个平台也是不同的。
现代 DevOps 对 C 和 C ++ 的要求
鉴于以上列出的挑战,C 和C ++ 项目的现代 DevOps 实践和工具要求是什么?
管理和重用二进制文件:每次构建源代码都非常昂贵,容易出错并且不适合大型项目。重复使用二进制文件应该对不同的平台保持一致,并处理 ABI 兼容性问题。
管理单个存储库或服务器中的所有二进制文件:在开发人员或 DevOps 工程师中,在多个系统(例如 Debian 中的 apt,RH 中的 rpm,Windows 中的 nuget / choco 和 OSX 中的 pkg )中创建和维护软件包非常昂贵。能够从同一地点存储,管理和检索二进制文件是一个非常简单的基础设施和流程,因此节省了大量的时间。
适应从传统到最新的任何构建系统,平台和工具:如果每个人都使用单一构建系统,创建良好的 CI 和包管理将会简单得多。但是思考项目可以迁移到构建系统显然是天真的。将一些图书馆或项目迁移到新的构建系统的成本是巨大的投资,可能需要几个月的时间。流程和工具必须能够适应处理任何构建系统和现有的遗留工具。
与其他工具有良好的集成:不仅要构建系统,而且工具必须尽可能简单地与其他工具集成。例如,为 Linux / OSX 和 Windows 提供的 Travis 和 Appveyor 云服务等持续集成平台,非常受欢迎,可用于开放源码软件项目,以及 Jenkins,这是业界专有代码的实际标准。此外,工具必须与流行的 IDE (例如 Visual Studio 或 XCode )以及测试,覆盖,静态分析,多控制版本系统等完美集成。
Conan C / C ++ 包管理案例研究
Conan 是一个免费的 OSS C / C ++ 包管理器。它是分布式的,可移植的和多平台的,运行在许多操作系统上,比如 Windows,Linux,Mac,BSD 和 Solaris ,而且针对任何可能的 C / C ++ 应用程序。
Conan 如何满足上述要求:
Conan 能够为任何数量的不同配置构建,上传和共享二进制文件。它管理二进制文件,根据需要为每个相应的平台检索正确的文件,并且还可以根据需要从源代码构建,最大限度地减少 ABI 不兼容问题。
Conan软件包和预先构建的二进制文件可以上传到服务器并在其中共享。可以使用JFrog Artifactory Pro 在团队内或公司范围内私下共享。如果需要,可以使用 JFrog Bintray 在全球范围内分发Conan软件包,包括私有或公共存储库,完全免费用于 OSS 项目。所有支持平台的所有不同二进制文件都可以在同一台服务器上进行上传和管理,界面完全相同。
Conan 与任何构建系统一起工作。它包括与 CMake,Visual Studio,Makefiles,SCons,QMake 等等的内置集成。它还具有良好的用户端可扩展性,可以使用“ 生成器 ” 来支持任何其他构建系统。这意味着可以开发扩展以将柯南依赖关系模型转换为任何构建系统格式,并生成可由这些构建系统读取的文件。
Conan 可以很好地与不同的 CI 系统集成,包括 Travis,Appveyor 和 GitLab 的特定工具和帮助程序,还可以在 Jenkins Artifactory 插件中使用自定义的 DSL 。
Conan 软件包的配置是用 Python 编写的,所以它们可以用标准的 Python 语法很容易地自定义行为,使用户可以轻松地插入本文前面提到的许多不同的工具:静态分析,测试,代码覆盖面,等等。
让我们来看看从 DevOps 的角度来看一个典型的流程,来说明 Conan 如何工作。首先,开发人员将某些源代码更改为特定的库( LibB ),并将这些更改提交给共享版本控制系统服务器( Git Repo )。
![1514961718601968.png 1.png](https://i-blog.csdnimg.cn/blog_migrate/1cbbbf00ba9e531987b4bcd6b9804971.jpeg)
图表:C / C ++与 Conan,Artifactory 和 Jenkins(构建系统:CMake,VS,MakeFile)的 CI 循环示例。
然后,CI 系统监控 Git 存储库,理想情况下使用 Git hooks ,因为不建议进行轮询,并为其启动一个工作(Jenkins)。下面是一个如何用 Jenkins pipeline 定义的例子:
![1514961728409248.jpg 2.jpg](https://i-blog.csdnimg.cn/blog_migrate/f3cabbe0b979a934a4d749412f02b367.jpeg)
上例中的第一个阶段很简单。第二个阶段调用内部分割成两个子任务的包创建:
LibB 依赖关系以二进制形式被检索,以避免从源重建并浪费时间。
所有传递依赖关系的依赖关系图被构建,并且一个或多个文件被写入。在这个例子中,LibB 使用 CMake 作为构建系统,Conan 生成一个 “conanbuildinfo.cmake” 文件,其中包含所有的依赖关系信息,以便 LibB 查找并正确链接到它们。然后 Conan 将调用 LibB 生成系统为它生成一个二进制包。最后,在上一个 Jenkinsfile 阶段 “Upload packages” 中,生成的二进制文件被上传到 Conan 包服务器(JFrog Artifactory)。LibB 二进制包可立即供开发人员或其他 CI 作业使用。 此示例说明了 “conan create” 命令中使用 “-s arch = x86” 参数指定的 32 位系统结构的构建配置。请注意,可以使用针对不同编译器,操作系统,体系结构等的并行任务和作业并行构建许多其他配置。
结论
与其他正在开发的编程语言相比,C 和 C ++ 有更多的挑战。这也使得尝试实施现代 DevOps 时更具挑战性,但并非不可能。C / C ++ 已经被忽略了,现在是时候更新工具来满足开发人员的需求了。您在 C / C ++ 项目中实施了哪些 DevOps 原则和实践呢?
作者:靳日阳