Jenkins如何自动化构建C语言项目?

99ANYc3cd6
预计阅读时长 34 分钟
位置: 首页 C语言 正文

核心概念:为什么用 Jenkins 管理C语言项目?

对于 C 语言项目,手动编译、测试和发布流程不仅耗时,而且容易出错,Jenkins 可以自动化这一切:

jenkins c语言
(图片来源网络,侵删)
  • 自动化构建:每次代码提交到代码仓库(如 Git),Jenkins 就能自动拉取最新代码并编译。
  • 自动化测试:在构建成功后,自动运行单元测试、集成测试,确保代码质量。
  • 快速反馈:如果构建或测试失败,Jenkins 会立即通知开发者,帮助快速定位问题。
  • 版本管理:可以自动为每次成功的构建创建一个可部署的软件包(如 .deb.zip),并记录版本号。
  • 标准化环境:确保所有构建和测试都在一个干净、一致的环境中运行,避免“在我电脑上能跑”的问题。

环境准备

在开始之前,你需要准备以下环境:

1 Jenkins 服务器

  • 安装 Jenkins,你可以使用 Docker、直接在 Linux 上安装,或者使用 Jenkins 的官方镜像。
  • 确保你的 Jenkins 服务器上安装了必要的工具,这些工具将在 Jenkins Job 中使用:
    • Git: 用于拉取代码。
    • Make / CMake: 用于构建 C/C++ 项目,我们将以 Make 为例。
    • GCC / Clang: C 语言编译器。
    • GDB (可选): 调试工具。
    • Valgrind (可选): 内存错误检测工具。
    • Gcov / Lcov (可选): 代码覆盖率工具。
    • Doxygen (可选): 文档生成工具。

在 Jenkins 服务器上安装这些工具的命令(以 Ubuntu 为例):

sudo apt-get update
sudo apt-get install -y git make gcc gdb valgrind lcov doxygen

2 代码仓库

  • 创建一个 Git 仓库(例如在 GitHub, GitLab, Gitee 上),并上传一个简单的 C 语言项目作为示例。

3 示例 C 语言项目

我们创建一个简单的计算器项目,包含一个源文件 calculator.c,一个头文件 calculator.h,一个测试文件 test_calculator.c,以及一个 Makefile

项目结构:

jenkins c语言
(图片来源网络,侵删)
my-c-project/
├── src/
│   ├── calculator.c
│   └── calculator.h
├── tests/
│   └── test_calculator.c
├── Makefile
└── README.md

src/calculator.h

#ifndef CALCULATOR_H
#define CALCULATOR_H
int add(int a, int b);
int subtract(int a, int b);
#endif // CALCULATOR_H

src/calculator.c

#include "calculator.h"
int add(int a, int b) {
    return a + b;
}
int subtract(int a, int b) {
    return a - b;
}

tests/test_calculator.c

#include <stdio.h>
#include <assert.h>
#include "calculator.h"
void test_add() {
    assert(add(1, 2) == 3);
    assert(add(-1, 5) == 4);
    printf("test_add passed!\n");
}
void test_subtract() {
    assert(subtract(5, 3) == 2);
    assert(subtract(2, 5) == -3);
    printf("test_subtract passed!\n");
}
int main() {
    test_add();
    test_subtract();
    printf("All tests passed successfully!\n");
    return 0;
}

Makefile

jenkins c语言
(图片来源网络,侵删)
# Compiler
CC = gcc
CFLAGS = -Wall -Wextra -g
# Directories
SRC_DIR = src
TEST_DIR = tests
BUILD_DIR = build
# Source and object files
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
OBJ_FILES = $(SRC_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
TEST_FILE = $(TEST_DIR)/test_calculator.c
TEST_EXEC = $(BUILD_DIR)/test_calculator
# Default target
all: $(TEST_EXEC)
# Rule to compile source files into object files
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR)
    $(CC) $(CFLAGS) -c $< -o $@
# Rule to link object files into the test executable
$(TEST_EXEC): $(OBJ_FILES) | $(BUILD_DIR)
    $(CC) $(CFLAGS) $^ -o $@
# Rule to create the build directory
$(BUILD_DIR):
    mkdir -p $(BUILD_DIR)
# Rule to run tests
test: $(TEST_EXEC)
    ./$(TEST_EXEC)
# Rule to clean build artifacts
clean:
    rm -rf $(BUILD_DIR)
.PHONY: all test clean

在 Jenkins 中创建流水线

我们将使用 Jenkins 的声明式流水线 来自动化我们的项目。

1 安装必要插件

在 Jenkins 管理界面,进入 Manage Jenkins -> Manage Plugins -> Available,安装以下插件:

  • Git Plugin: 用于与 Git 仓库交互。
  • Pipeline Utility Steps: 提供一些有用的步骤,如 readJSON
  • HTML Publisher Plugin: 用于发布测试报告(可选)。

2 创建 Jenkins Item

  1. 在 Jenkins 首页,点击 New Item
  2. 输入一个名称(c-project-pipeline),选择 Pipeline,然后点击 OK
  3. 在配置页面,滚动到 Pipeline 部分。

3 编写 Jenkinsfile

Pipeline 部分,选择 Pipeline script from SCM

  • SCM: 选择 Git
  • Repository URL: 填写你的 Git 仓库地址。
  • Credentials: 如果你的仓库是私有的,添加你的 Git 凭据。
  • Branch Specifier: */main*/master

Script Path 字段中,输入 Jenkinsfile,我们将在项目的根目录下创建这个文件。

Jenkinsfile (声明式流水线)

pipeline {
    // 定义 Agent(执行环境)。'any' 表示在任何可用的 Jenkins agent 上运行。
    agent any
    // 定义环境变量,可以在整个流水线中使用。
    environment {
        // 定义项目名称,方便构建产物命名
        PROJECT_NAME = "my-c-project"
        // 定义构建输出的目录
        BUILD_DIR = "build"
    }
    // 定义流水线的阶段
    stages {
        // 1. 拉取代码
        stage('Checkout') {
            steps {
                script {
                    // 清理工作区,确保每次都是最新的代码
                    cleanWs()
                    // 从 SCM 拉取代码
                    checkout scm
                }
                echo '代码已成功拉取到 Jenkins 工作区。'
            }
        }
        // 2. 编译项目
        stage('Build') {
            steps {
                echo '开始编译项目...'
                // 执行 make all 命令
                sh 'make all'
                echo '项目编译成功!'
                // (可选) 显示编译出的可执行文件
                sh "ls -l ${BUILD_DIR}/"
            }
        }
        // 3. 运行单元测试
        stage('Test') {
            steps {
                echo '开始运行单元测试...'
                // 执行 make test 命令
                sh 'make test'
                echo '单元测试完成!'
                // (可选) 运行 Valgrind 进行内存检查
                echo '开始运行 Valgrind 内存检查...'
                sh "valgrind --leak-check=full --show-leak-kinds=all --verbose ${BUILD_DIR}/test_calculator"
                echo 'Valgrind 内存检查完成!'
            }
        }
        // 4. (可选) 代码覆盖率分析
        stage('Coverage') {
            steps {
                echo '开始生成代码覆盖率报告...'
                // 修改 Makefile 以生成覆盖率信息,或者直接使用 gcov
                // 这里我们假设 Makefile 已经配置好,可以生成 gcov 文件
                sh 'make coverage' 
                echo '覆盖率报告生成完毕。'
                // (可选) 使用 Lcov 生成 HTML 报告并发布
                // publishHTML([
                //     allowMissing: false,
                //     alwaysLinkToLastBuild: true,
                //     keepAll: true,
                //     reportDir: 'coverage',
                //     reportFiles: 'index.html',
                //     reportName: 'Code Coverage Report'
                // ])
            }
        }
        // 5. (可选) 构建软件包
        stage('Package') {
            steps {
                echo '开始构建软件包...'
                // 这里可以创建一个 .tar.gz 或 .deb 文件
                //  sh 'tar -czf ${PROJECT_NAME}-${BUILD_ID}.tar.gz src/ Makefile'
                // 或者使用 `dpkg-deb` 来创建 .deb 包
                def package_name = "${PROJECT_NAME}-${env.BUILD_ID}.tar.gz"
                sh "tar -czf ${package_name} src/ tests/ Makefile"
                echo "软件包 ${package_name} 创建成功!"
                // 将构建产物归档,以便后续下载
                archiveArtifacts artifacts: "${package_name}", fingerprint: true
            }
        }
    }
    // 定义后置操作,无论成功或失败都会执行
    post {
        // 成功时执行
        success {
            echo '流水线执行成功!'
            // 可以在这里发送成功通知,例如邮件、Slack等
        }
        // 失败时执行
        failure {
            echo '流水线执行失败!'
            // 可以在这里发送失败通知
        }
        // 总是执行,例如清理工作区
        always {
            echo '流水线执行完毕,正在清理工作区...'
            // cleanWs() // 如果需要,可以取消注释
        }
    }
}

运行和解释

  1. 保存 Jenkins Item 的配置。
  2. 回到项目首页,点击 "Build Now"

Jenkins 会开始执行你的流水线,你可以在 "Console Output" 中查看详细的执行日志。

流水线解释:

  • agent any: Jenkins 会在任何一个可用的节点上执行这个任务。
  • environment { ... }: 定义了 PROJECT_NAMEBUILD_DIR,这样在后续步骤中可以直接使用 ${PROJECT_NAME}${BUILD_DIR},方便维护。
  • stages { ... }: 流水线的核心,包含多个 stage
    • Checkout: 使用 checkout scm 语句,这是 Jenkins Pipeline 插件提供的标准方式,它会自动从配置的 SCM 仓库拉取代码。
    • Build: 使用 sh 'make all' 执行编译。sh 步骤用于在 shell 中执行命令。
    • Test: 使用 sh 'make test' 运行我们编写的测试用例,我们集成了 valgrind 来检查内存泄漏。
    • Coverage: 这是一个可选的高级步骤,用于衡量测试覆盖了多少代码,你需要修改 Makefile 来支持生成 .gcov 文件,然后使用 lcov 工具将其转换为 HTML 报告。
    • Package: 将构建产物(源码、Makefile等)打包成一个 .tar.gz 文件,并使用 archiveArtifacts 将其保存到 Jenkins 服务器,方便下载。
  • post { ... }: 定义了流水线结束后的操作。
    • success / failure: 可以在这里集成通知系统,比如当构建失败时通过邮件或 Slack 通知开发人员。
    • always: 无论成功失败都会执行,通常用于清理工作区 cleanWs()

进阶与最佳实践

1 使用 Docker 确保环境一致性

为了避免 "在我电脑上能跑" 的问题,最佳实践是使用 Docker,你可以创建一个 Dockerfile 来定义一个纯净的构建环境。

Dockerfile

FROM ubuntu:22.04
# 安装构建和测试所需的工具
RUN apt-get update && apt-get install -y \
    git \
    make \
    gcc \
    gdb \
    valgrind \
    lcov \
    doxygen \
    && rm -rf /var/lib/apt/lists/*
# 设置工作目录
WORKDIR /workspace
# 默认命令
CMD ["/bin/bash"]

然后修改你的 Jenkinsfile,使其在 Docker 容器中运行:

pipeline {
    agent {
        dockerfile {
            filename 'Dockerfile'
            dir '.' // Dockerfile 所在的目录
        }
    }
    stages {
        // ... (其他阶段保持不变)
    }
}

这样,Jenkins 会自动构建这个 Docker 镜像,并在容器中执行你的整个流水线,确保了环境的一致性。

2 代码质量分析 (静态分析)

集成像 CppcheckClang Static Analyzer 这样的静态分析工具,可以在不运行代码的情况下发现潜在的错误。

Makefile 中添加一个 check target:

# ... (其他内容)
check:
    cppcheck --enable=all --suppress=missingIncludeSystem src/
.PHONY: all test clean check

然后在 JenkinsfileTest 阶段之前添加一个新的 Static Analysis 阶段:

stage('Static Analysis') {
    steps {
        echo '开始进行静态代码分析...'
        sh 'make check'
        echo '静态代码分析完成。'
    }
}

3 部署

部署阶段通常与你的目标环境相关。

  • 部署到服务器: 使用 ssh 步骤插件将编译好的二进制文件或软件包复制到远程服务器。
  • 部署到 Docker 镜像: 在 Package 阶段构建一个包含你应用的 Docker 镜像,并推送到镜像仓库(如 Docker Hub, Harbor)。

通过以上步骤,你就拥有了一个功能完善的、用于 C 语言的 Jenkins 持续集成/持续交付流水线,它可以极大地提高开发效率和软件质量。

-- 展开阅读全文 --
头像
C语言warning为何总出现?如何有效解决?
« 上一篇 04-21
c语言 context
下一篇 » 04-21

相关文章

取消
微信二维码
支付宝二维码

目录[+]