使用 Jenkins、Docker 和 Conan 进行 C/C++ 嵌入式设备的持续集成
我们都知道在软件开发过程中持续集成的重要性:运行单元或集成测试、支持不同的平台/设备、支持不同版本的编译器或库以及始终为新版本做好准备,这些都是一些原因。
在最终部署的相同平台上开发最终应用程序很好,并且使事情变得更容易,因为您已经完成了设置,安装了所有所需的工具,并且您甚至可以在您的机器上模拟应用程序的最终部署目标进行测试。
嵌入式设备编程则完全不同。通常,您必须设置一种带有交叉编译所需变量的固定环境,以及准备使用的交叉编译器和工具链。这听起来并不痛苦,并且 Conan 已经具备了一些有助于此类设置的功能:安装程序包、用于共享脚本的 Python 包、virtualenv 生成器 和 conan config install。
但是,当您开始支持不同的设备时,很容易弄乱您的环境配置,并且您可能会浪费大量时间检查所有内容是否都正确设置。为了解决这个问题,我们最近在我们的文档中发布了一个操作指南,展示了一系列可用于交叉构建的 Docker 镜像。
在这篇文章中,我们将向您展示如何使用这些镜像在 Jenkins 中设置持续集成作业,以进行 ARM 设备的交叉构建并创建最终应用程序。
将应用程序作为 Conan 包
我们将构建的应用程序是一个简单的闪烁程序,它使用 WiringPi 库来连接 Raspberry Pi 的 GPIO 引脚。
#include <iostream>
#ifdef WIRINGPI
#include <wiringPi.h>
#endif
int main (void)
{
#ifdef WIRINGPI
wiringPiSetup();
pinMode(0, OUTPUT);
#endif
while(1)
{
std::cout << "HIGH" << std::endl;
#ifdef WIRINGPI
digitalWrite(0, HIGH);
delay(500);
#endif
std::cout << "LOW" << std::endl;
#ifdef WIRINGPI
digitalWrite(0, LOW);
delay(500);
#endif
}
return 0;
}
如您所见,这是一个非常简单的应用程序,它包含一些额外的定义以避免在不需要时使用 WirignPi 库。我们最近将 wiringpi/2.46@conan/stable 包含在 conan-center 中,因此我们将将其用作可选依赖项。
您可以在此处查看完整的项目:https://github.com/danimtb/conan-blink-app
使用 armv7 配置时,配方有一些设置
from conans import ConanFile, CMake
class BlinkAppConan(ConanFile):
name = "BlinkApp"
version = "0.1"
license = "MIT"
description = "Blink application"
settings = "os", "compiler", "build_type", "arch"
url = "https://github.com/conan-community/conan-blink-app.git"
exports_sources = "CMakeLists.txt", "main.cpp", "LICENSE"
generators = "cmake"
def build(self):
cmake = CMake(self)
if self.settings.arch == "armv7":
cmake.definitions["WIRINGPI"] = True
cmake.configure()
cmake.build()
def package(self):
self.copy("blinkapp", src="bin", dst="bin")
def requirements(self):
if self.settings.arch == "armv7":
self.requires("wiringpi/2.46@conan/stable")
def deploy(self):
self.copy("blinkapp", src="bin", dst="bin")
配方非常简单,只需要注意几点
- 在
build()
和requirements()
中有一个条件语句:如前所述,这是为了管理 WiringPi 的可选依赖项。 package()
获取应用程序以对其进行打包。deploy()
用于 使应用程序可从命令行部署:conan install BlinkApp/0.1@danimtb/stable
在 CI 机器上设置 Jenkins 和 Artifactory
对于这篇文章,我使用的是一台 Linux 机器,Jenkins、Docker 和 Artifactory 将在此机器上运行。以下是一些您需要的东西
-
Jenkins:我们将使用 Jenkins 自动化持续集成作业。
我使用以下步骤安装它
$ wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add - $ sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list' $ sudo apt-get update $ sudo apt-get install jenkins
查看其他平台的(安装步骤)[https://jenkins.io/doc/book/installing/]。
-
Artifactory:我们将将其用作我们的二进制存储库,用于检索和上传 Conan 包。
我选择了最新版本的 Artifactory Community Edition for C/C++,并选择了 zip 安装
- 下载 zip:https://www.conan.io/downloads.html
- 设置
JAVA_HOME=/usr/lib/jvm/default-java
- 启动 Artifactory:
bin/artifactory.sh
Artifactory 实例应可以通过以下地址访问:
localhost:8081/artifactory
。在配置步骤之后,创建一个名为 conan-local 的 Conan 存储库。 -
Jenkins Artifactory 插件:
当然,如果您使用 Jenkins 和 Artifactory,则应该利用此插件。在 Conan 的情况下,它使配置远程和凭据以及上传构建元数据变得更加容易。
按照安装步骤操作,并使用您的 Artifactory 凭据配置插件
-
Docker:当然,我们将使用 Docker,因此也需要在 CI 机器上安装它。
$ apt-get install docker-ee $ Docker -v > Docker version 1.13.1, build 092cba3
查看其他平台的安装步骤。
创建 Jenkins 管道
网上有一些相关信息介绍如何在 Docker 容器内运行 Jenkins 或如何使用 Docker 插件启动构建。但是,关于如何在 Docker 容器内使用 Jenkins 运行本地构建的信息并不多。
如果您使用命令行脚本进行构建,这应该不会太复杂,但是使用管道语法来控制阶段会更好。
您可以在此处查看创建此应用程序的 Jenkinsfile
def artifactory_name = "artifactory_local"
def artifactory_repo = "conan-local"
String docker_image = 'lasote/conangcc6-armv7'
node {
docker.image(docker_image).inside('-v /tmp/:/tmp/ -v /home/danimtb/:/home/conan/danimtb/ --net=host') {
def server = Artifactory.server artifactory_name
def client = Artifactory.newConanClient()
def serverName = client.remote.add server: server, repo: artifactory_repo
stage("Get project") {
checkout scm
}
stage("Get dependencies and create app") {
String strCommand = "create . danimtb/stable -pr /home/conan/.conan/profiles/default"
client.run(command: strCommand )
}
stage("Upload packages") {
String command = "upload BlinkApp* --all -r ${serverName} --confirm"
def b = client.run(command: command)
b.env.collect()
server.publishBuildInfo b
}
}
}
如您在 Jenkinsfile 中所见,我们正在设置已在 Jenkins Artifactory 插件中设置的 Artifactory 实例的名称和 Conan 存储库名称。我们不需要配置任何凭据或使用环境变量 CONAN_PASSWORD
。
在这种情况下,我们使用的 Docker 镜像是 lasote/conangcc6-armv7
,它将使用一些目录和网络映射来运行构建
docker.image(docker_image).inside('-v /tmp/:/tmp/ -v /home/danimtb/:/home/conan/danimtb/ --net=host')
/tmp/:/tmp/
:我们将在此处映射临时目录,因为它是 Jenkins 用于存储构建文件和元数据的目录。这对于保留这些信息非常有用,这样在 Docker 镜像停止后就不会被销毁。/home/danimtb/:/home/conan/danimtb/
:这只是本地目录的简单映射,允许克隆 SCM,因为在这种情况下我使用的是本地 git 存储库。--net=host
:这将容器的网络映射到主机的网络。这是将包上传到 Artifactory 所必需的。
这些镜像在内部准备了一个配置文件,可以将其作为目标体系结构。
String strCommand = "create . danimtb/stable -pr /home/conan/.conan/profiles/default"
client.run(command: strCommand )
触发构建
可以使用正常的 Jenkins 配置来触发构建。我使用了一个包含本地 git 存储库和多分支作业的自包含作业。这样,Jenkins 就可以检查存储库中的更改并触发作业。
多分支方法还有其他好处,例如,如果您有一个用于测试的分支和另一个稳定的分支,则可以自定义您的 Jenkinsfile,因为您可以在 conan create
步骤中提供的 user/channel
进行更改。
任何其他方法也都是有效的,例如,您可以使用 GitHub hook 触发构建,并具有与在 Travis CI 或 AppVeyor 中运行的 CI 构建相同的行为。
上传最终应用程序的包后,您可以使用简单的 conan install BlinkApp/0.1@danimtb/stable
命令在最终设备上测试应用程序,利用 deploy()
并将其用于诸如硬件在环测试之类的测试目的,或者创建安装程序/tarball 以交付应用程序及其资源。
结论
如您所见,最复杂的部分是配置 Jenkins 和 Artifactory 以运行完整的示例。但是,如果您已经有一个正在运行的实例,那么这应该非常简单。
这只是一个示例,说明您可以利用 Docker 和 Jenkins 做哪些事情,以及如何将其用于自动化包创建。这可以扩展到创建库、在下游依赖项出现新版本时更新包版本、运行包测试等。
您还可以使用所需的工具创建自己的 Docker 镜像。查看我们的 conan-docker-tools 存储库,从 Dockerfiles 中获取灵感。
希望您发现此示例对设置 CI 机器很有用,它包含开始自动化 Conan 包创建所需的一切!