- 引言
在项目构建的时候,我们经常会希望能够将git提交的分支信息和提交号(commitid)以及当前版本发布的tag信息作为版本号自动构建到程序里面,以便后续能够快速定位所运行的程序所定影的git源码的版本,从而快速发现和定位问题。
本文利用cmake的自动构建能力,来实现以下信息的自动提取并构建到程序中:
- 当前代码的git 分支名
- 当前代码的git 提交号(commitid)
- 当前代码的tag所设置的版本号
- 当前代码的构建时间
- 当前代码的构建号(buildno)
以下以c语言构成为例,展示了利用cmake自动生成config.h文件,从而将以上信息自动集成到c语言工程代码中的过程。
- 实现过程
2.1 工程目录规划
如下图:
其中有c语言源码都放在工程根目录下面,当然,对于比较大型的工程,可以对源码目录进行更细地拆分和规划,本demo只有一个test.c和config.h的c源码文件,因此源码目录从简;创建一个cmake目录,用来存放自定义的cmake模块文件;创建一个build目录,用来存放cmake运行所产生的输出文件。
2.2 c程序文件
test.c 文件的内容如下:
代码语言:javascript复制#include <stdio.h>
#include "config.h"
int main()
{
printf("version: %sn", PROJECT_VERSION);
printf("commit: %sn", GIT_COMMITID);
printf("branch: %sn", GIT_BRANCH);
printf("build time: %sn", BUILD_TIME);
printf("build no: %sn", BUILD_NO);
return 0;
}
程序非常简单,无需多言。
config.h文件的内容如下:
代码语言:javascript复制#ifndef __CONFIG_H__
#define __CONFIG_H__
#define PROJECT_VERSION "0-1-9-alpha"
#define GIT_COMMITID "51045662cffc38c5cf706090a6f0c3d16311d85e"
#define GIT_BRANCH "test"
#define BUILD_TIME "2024-07-10 10:45:29"
#define BUILD_NO "10"
#endif
这个config.h文件是用cmake自动构建出来的,不需要每次手工修改。以下是程序运行的效果:
代码语言:javascript复制stone:build$ ./gitversion
version: 0-1-9-alpha
commit: 51045662cffc38c5cf706090a6f0c3d16311d85e
branch: test
build time: 2024-07-10 10:45:29
build no: 10
2.3 CMakeLists.txt
代码语言:javascript复制cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(gitversion C)
# 设置自定义模块文件的所在路径
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
# 导入自定义模型文件
include(GitVersion)
include(BuildNumber)
# 获取程序的版本信息
fetch_version_from_git("GITVERSION")
# 获取程序的构建号
new_build_number("GITVERSION")
set(GITVERSION_VERSION "${GITVERSION_VERSION_MAJOR}-${GITVERSION_VERSION_MINOR}-${GITVERSION_VERSION_PATCH}-${GITVERSION_VERSION_STAGE}")
# 生成程序的构建时间
string(TIMESTAMP BUILD_TIME "%Y-%m-%d %H:%M:%S")
# 利用config.h.in模板文件自动生成config.h文件
configure_file("${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_SOURCE_DIR}/config.h" @ONLY)
# 创建构建目标为可执行程序
add_executable(${PROJECT_NAME} test.c)
2.4 GitVersion.cmake文件
代码语言:javascript复制# This module defines the following variables utilizing
# git to determine the parent tag. And if found the macro
# will attempt to parse them in the github tag format
#
# Useful for auto-versioning in our CMakeLists
#
# ${VARPREFIX}_VERSION_MAJOR - Major version.
# ${VARPREFIX}_VERSION_MINOR - Minor version
# ${VARPREFIX}_VERSION_PATCH - Patch number
# ${VARPREFIX}_VERSION_STAGE - Stage version
# ${VARPREFIX}_COMMITID - Git commitid
# ${VARPREFIX}_BRANCH - Git branch
#
# Example usage:
#
# fetch_version_from_git("test")
# message(" major=${test_VERSION_MAJOR}")
# message(" minor=${test_VERSION_MINOR}")
# message(" patch=${test_VERSION_PATCH}")
# message(" stage=${test_VERSION_STAGE}")
# message(" stage=${test_BRANCH}")
include(FindGit)
macro(fetch_version_from_git VARPREFIX)
# set our defaults.
set(${VARPREFIX}_VERSION_MAJOR 1)
set(${VARPREFIX}_VERSION_MINOR 0)
set(${VARPREFIX}_VERSION_PATCH 0)
set(${VARPREFIX}_VERSION_STAGE "alpha-dev")
find_package(Git)
if (GIT_FOUND)
# 获取git的提交号
execute_process(
COMMAND
${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY
${PROJECT_SOURCE_DIR}
RESULT_VARIABLE
GITRET
OUTPUT_VARIABLE
${VARPREFIX}_COMMITID
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (NOT GITRET EQUAL 0)
message(FATAL_ERROR "Failed to fetch current git commitid.")
endif()
# 获取git的分支名
execute_process(
COMMAND
${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY
${PROJECT_SOURCE_DIR}
RESULT_VARIABLE
GITRET
OUTPUT_VARIABLE
${VARPREFIX}_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (NOT GITRET EQUAL 0)
message(FATAL_ERROR "Failed to fetch current git branch name.")
endif()
# 通过git的tag获取工程的版本号
execute_process(
COMMAND
${GIT_EXECUTABLE} describe --abbrev=0 --always
WORKING_DIRECTORY
${PROJECT_SOURCE_DIR}
RESULT_VARIABLE
GITRET
OUTPUT_VARIABLE
GITVERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# 匹配(.、_、-),并替换为; 即将字符串分割成字符串列表
# 获取到的版本信息如:release-2.2.1-alpha, 总共5段
string(REGEX REPLACE "[\._-]" ";" VERSION_LIST "${GITVERSION}")
if(VERSION_LIST)
list(LENGTH VERSION_LIST VERSION_LIST_LENGTH)
endif()
if ((GITRET EQUAL 0) AND (VERSION_LIST_LENGTH EQUAL 5))
list(GET VERSION_LIST 1 _MAJOR)
list(GET VERSION_LIST 2 _MINOR)
list(GET VERSION_LIST 3 _PATCH)
list(GET VERSION_LIST 4 _STAGE)
set(_DEFAULT_VERSION "${${VARPREFIX}_GIT___VERSION_MAJOR}.${${VARPREFIX}_GIT___VERSION_MINOR}.${${VARPREFIX}_GIT___VERSION_PATCH}-${${VARPREFIX}_GIT___VERSION_STAGE}")
set(_GIT_VERSION "${_MAJOR}.${_MINOR}.${_PATCH}-${_STAGE}")
if (${_DEFAULT_VERSION} VERSION_LESS ${_GIT_VERSION})
set(${VARPREFIX}_VERSION_MAJOR ${_MAJOR})
set(${VARPREFIX}_VERSION_MINOR ${_MINOR})
set(${VARPREFIX}_VERSION_PATCH ${_PATCH})
set(${VARPREFIX}_VERSION_STAGE ${_STAGE})
endif()
endif()
endif()
endmacro()
以上fetch_version_from_git 宏利用git命令来获取工程的git提交号、分支名、以及版本号。其中版本号是在所打的git tag中获取的,tag名称的格式如下:gitversion-0.1.9-alpha。总共分为3个部分,第一个部分是工程名;第二个部分是版本号,要求格式为主版本号.次版本号.PATCH版本号;第三部分是版本发布阶段,包括rc、alpha、beta、alpha-dev、release、stable等等。
最后获取到的信息放在对应的变量中,其中变量名的前缀由宏的调用者传入,如下:
代码语言:javascript复制 ${VARPREFIX}_VERSION_MAJOR - Major version.
${VARPREFIX}_VERSION_MINOR - Minor version
${VARPREFIX}_VERSION_PATCH - Patch number
${VARPREFIX}_VERSION_STAGE - Stage version
${VARPREFIX}_COMMITID - Git commitid
${VARPREFIX}_BRANCH - Git branch
2.5 BuildNumber.cmake文件
代码语言:javascript复制# This module defines the following variables to denote
# new generated buildno
#
# ${VARPREFIX}_BUILD_NUMBER - New generated BuildNo
#
# If the build_number.txt file does not exist under
# ${CMAKE_CURRENT_SOURCE_DIR} directory, a new build_number.txt
# will be created and the build no will be set to 1, otherwise
# the build no will increment one by one everytime cmake command
# is executed.
#
# Example usage:
#
# new_build_number("test")
# message(" buildno=${test_BUILD_NUMBER}")
macro(new_build_number VARPREFIX)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/build_number.txt")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/build_number.txt" ${VARPREFIX}_BUILD_NUMBER)
string(STRIP ${${VARPREFIX}_BUILD_NUMBER} BUILD_NUMBER)
else()
set(${VARPREFIX}_BUILD_NUMBER 1)
endif()
# 将 build number 加1
math(EXPR NEW_BUILD_NUMBER "${${VARPREFIX}_BUILD_NUMBER} 1")
# 写入新的 build number 到文件
file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/build_number.txt" "${NEW_BUILD_NUMBER}")
endmacro()
以上宏代码会读取build_number.txt文件中的构建号,加1后从新写回build_number.txt中;如果build_number.txt文件不存在,则会创建一个新的build_number.txt,并将构件号设置为1。读取的构建号放在${VARPREFIX}_BUILD_NUMBER中。