基于cmake为项目自动获取git分支tag的版本号和commitid

2024-07-20 11:29:13 浏览数 (3)

  1. 引言

  在项目构建的时候,我们经常会希望能够将git提交的分支信息和提交号(commitid)以及当前版本发布的tag信息作为版本号自动构建到程序里面,以便后续能够快速定位所运行的程序所定影的git源码的版本,从而快速发现和定位问题。  

本文利用cmake的自动构建能力,来实现以下信息的自动提取并构建到程序中:

  • 当前代码的git 分支名
  • 当前代码的git 提交号(commitid)
  • 当前代码的tag所设置的版本号
  • 当前代码的构建时间
  • 当前代码的构建号(buildno)

  以下以c语言构成为例,展示了利用cmake自动生成config.h文件,从而将以上信息自动集成到c语言工程代码中的过程。

  1. 实现过程

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中。

0 人点赞