项目配置与编译自动化
嵌入式设备公司的一个产品线不可能只出一款产品,必定会有各种客制化功能以适应不同场景,在基础版上衍生出多个版本。对软件来说,最佳方案是同一套代码进行不同的配置,编译出适配不同机型的软件,通用bug一处解决则全部机型的都可以修复,当然一旦出事也是一起遭殃。
一套软件适配多个项目,在代码中必然有项目宏,在不同的项目宏下,有不同的功能配置和默认参数。代码实现时尽量使用功能宏,实在无法兼容的则提取项目特有的,单独一个文件实现,每个项目互不影响,要实现编译自动化,还需要脚本配合。
如有甲乙丙3个项目,在ABC 三个标准功能上进行客制化,形式如下:
基线版本={A+B+C}
甲={A1+B1+C1}
乙={A2+B2+C2}
丙={A3+B3+C3}
如果差异很小,在宏文件分别定义甲乙丙3个项目,每个项目下有3个子功能宏表示不同的小功能。
项目=甲/乙/丙三选一
#if 甲
A1+B1+C1
#elif 乙
A2+B2+C2
#elif 丙
A3+B3+C3
#endif
源码是同一套,编译脚本支持传入参数指定项目名,这样执行不同的脚本,编译出不同的项目软件。如果差异很大,单纯的功能宏已经无法区分,假如甲乙丙三个项目分别提取主要差异,在A.c、B.c、C.c中实现客制化的ABC功能,则对编译.c文件路径进行修改,在加编译路径时加项目宏
SRC+=./xx/xx/{项目名}/A.c
SRC+=./xx/xx/{项目名}/B.c
SRC+=./xx/xx/{项目名}/C.c
每个项目分别实现自身功能,但是在编译时统一文件名,添加编译路径时根据不同的项目,从三个同名文件中选择一个进行编译。
如果项目特殊,比如驱动配置或者非.c编译文件,则可以使用拷贝机制,在进入正式编译前,拷贝该项目对应的文件到指定位置,然后再编译。如甲乙丙有3个端口配置文件夹。
./端口配置 (最终编译的只有这个文件夹)
./端口配置_甲
./端口配置_乙
./端口配置_丙
开始编译前,使用脚本复制对应项目的配置文件夹,如编译丙项目,则将“端口配置_丙”覆盖到“端口配置”,再进行后续编译。
对于简单的参数差异,尽量都合并在同一个.h,不需要脚本,直接编码如下在 .param.h文件
#if 甲
#include “param_甲.h”
#elif 乙
#include “param_乙.h”
#elif 丙
#include “param_丙.h”
#endif
如果操作再骚点,.c文件也可以使用#include。
实现了项目配置,脚本怎么操作呢?需要结合编译环境,如果是windows,一般使用.bat批处理,如果是linux环境则使用shell脚本,有些特殊的项目编译使用perl脚本。不管是什么环境,最终需要实现的功能都一样。识别输入的项目名参数,用它去匹配不同的脚本动作,进行复制或者设定编译路径。以windows下的bat文件为例,可以参考如下框架。
::编译指令
::mk [project] or mk
@echo off
:START
if not "1%~1%"=="1" goto check1
::无项目名参数
:select_project
ECHO.
ECHO *******************************************************
ECHO 1. project_A
ECHO 2. project_B
ECHO 3. project_C
ECHO *******************************************************
set /p userInput=选择项目序号(Q退出):
if /i "%userInput%"=="1" goto select_project_A
if /i "%userInput%"=="2" goto select_project_B
if /i "%userInput%"=="3" goto select_project_C
if /i "%userInput%"=="Q" goto END
echo 你究竟要编译哪个项目?
goto :select_project
:check1
if "1%~2%"=="1" goto check2
echo 只能输入一个项目名参数
goto :select_project
:check2
::echo %~dp0
::判断输入的项目名
if /i "%1%"=="project_A" goto select_project_A
if /i "%1%"=="project_B" goto select_project_B
if /i "%1%"=="project_C" goto select_project_C
echo 项目名不存在,请重新选择
goto select_project
:select_project_error_log
set mtk_project=HENIT2503D_11C
IF EXIST .\build\%mtk_project%\err.log DEL .\build\%mtk_project%\err.log /q
findstr /S Error: .\build\%mtk_project%\bootloader.log >>.\build\%mtk_project%\err.log
findstr /S Error: .\build\%mtk_project%\bootloader_ext.log >>.\build\%mtk_project%\err.log
findstr /S Error: .\build\%mtk_project%\log\*.log >>.\build\%mtk_project%\err.log
findstr /S Error: .\build\%mtk_project%\MT6261.log >>.\build\%mtk_project%\err.log
ECHO. >> .\build\%mtk_project%\err.log
.\build\%mtk_project%\err.log
goto end
::根据项目配置参数 xxx=xxx表示按实际添加变量
:select_project_A
set project=A
set xxx=aaa
goto CompileCopy
:select_project_B
set project=B
set xxx=bbb
goto CompileCopy
:select_project_C
set project=C
set xxx=ccc
goto CompileCopy
:CompileCopy
::按实际需求复制项目客制化文件
::自由扩展
::编译
goto END
:paramError
echo 输入参数错误无法执行
goto END
:END
ping -n 1 127.0.0.1>nul
::echo mk执行结束
先使用txt记事本编辑,再重命名修改后缀为bat即可双击运行。
编译完成后,将生成的有效的bin文件复制到特定文件夹,这样发布软件就不需逐个复制文件。流程是如此,实际项目中可能是几个方式混合使用,但思路一致,就是让编译器从多个并列项目代码中,选择一份进行编译;一套软件匹配多个项目,减少维护工作量。