基于Jenkins与Keil的嵌入式CI/CD实践——自动化构建与版本归档(三)
1. Jenkins与Keil自动化构建环境搭建搞嵌入式开发的朋友应该都经历过这样的痛苦每次代码更新后都要手动打开Keil点编译遇到团队协作时版本混乱更是家常便饭。去年我们团队接手的一个RT1052项目就深受其害直到用Jenkins搭建了自动化构建流水线。下面我就把踩坑后总结的最佳实践分享给大家。首先需要准备以下环境Jenkins服务器建议直接使用Docker部署避免环境依赖问题Keil MDK需要确保已安装且license有效Git版本控制推荐GitLab或GitHub托管代码配置Jenkins任务时这三个参数最关键UV_PRO_PATH项目路径/your_project.uvprojx KEIL_PATHC:/Keil_v5/UV4/UV4.exe BUILD_LOGbuild_log.txt实测发现Windows环境有个大坑Jenkins默认使用系统账户运行可能导致Keil找不到license。解决方法是在服务配置里改成当前用户登录运行打开services.msc找到Jenkins服务在登录选项卡选择此账户2. 构建触发策略的智能配置我们团队试过三种触发方式各有适用场景定时构建适合做每日集成triggers { cron(H 2 * * *) // 每天凌晨2点 }Git Hook触发最实用代码push后自动构建。需要在Git仓库配置webhookURL: http://jenkins/git/notifyCommit?url仓库地址手动触发留给测试人员用建议加参数化构建parameters { choice(name: BUILD_TYPE, choices: [Debug, Release], description: 选择构建类型) }特别提醒Keil工程建议开启Build Output选项中的Create Batch File这样可以直接用生成的bat文件作为构建脚本比调用UV4.exe更稳定。3. 版本号自动化生成方案固件版本管理是很多团队忽视的重灾区。我们的方案是将Git commit hash自动注入代码FOR /F delims %%i IN (git rev-list HEAD -n 1) DO ( echo #define FIRMWARE_VERSION 1.0.%%i version.h )进阶版可以结合语义化版本控制在Jenkinsfile里这样写stage(Version) { steps { script { def tag sh(script: git describe --tags, returnStdout: true).trim() def hash sh(script: git rev-parse --short HEAD, returnStdout: true).trim() writeFile file: src/version.h, text: #define VERSION ${tag} #define BUILD ${env.BUILD_NUMBER} #define COMMIT ${hash} } } }这样生成的固件包含完整版本信息调试时用show_version命令就能看到App v1.2.3 (build#45) Commit: a1b2c3d Built: 2023-08-20 14:304. 构建产物归档的工业级实践构建成功后的固件归档要考虑三个维度版本追溯包含完整构建信息空间效率压缩存储历史版本快速检索合理的目录结构这是我们优化后的归档脚本$version git rev-parse --short HEAD $date Get-Date -Format yyyyMMddHHmmss $artifacts ( output/firmware.hex, docs/release_notes.pdf ) Compress-Archive -Path $artifacts -DestinationPath archive/$($date)_$version.zip配套的Jenkins配置要注意在Post-build Actions中添加Archive the artifacts设置保留策略避免磁盘爆满publisher { archiveArtifacts { pattern: archive/*.zip onlyIfSuccessful: true allowEmptyArchive: false } buildDiscarder { strategy { logRotator { daysToKeepStr: 30 numToKeepStr: 100 } } } }5. 构建失败的智能处理Keil编译出错时Jenkins默认只会显示Build failed。我们通过日志分析实现了精准报错def analyze_keil_log(logfile): errors [] with open(logfile) as f: for line in f: if error in line.lower(): errors.append(line.strip()) return errors[:5] # 返回前5个错误在Jenkinsfile中添加错误处理阶段post { always { script { def log readFile(build_log.txt) if (currentBuild.result FAILURE) { def errors analyze_keil_log(build_log.txt) emailext body: 构建失败主要错误 ${errors.join(\n)} 完整日志见${env.BUILD_URL}console , subject: [紧急]固件构建失败, to: teamexample.com } } } }6. 多环境构建的矩阵策略当需要同时编译Debug和Release版本时推荐使用Jenkins的Matrix功能matrix { axes { axis { name BUILD_TYPE values Debug, Release } } stages { stage(Build) { steps { bat ${KEIL_PATH} -j0 -b \ -r ${UV_PRO_PATH} \ -o ${WORKSPACE}/build_${BUILD_TYPE}.log \ ${BUILD_TYPE} } } } }配合Keil的Target配置可以实现Debug版本开启优化等级O0Release版本开启O3优化不同版本使用独立的内存布局最后给个实用小技巧在Jenkins全局工具配置中添加Keil的路径这样所有Job都能通过tool Keil引用避免硬编码路径。