zeerd's blog         Search     Categories     Tags     Feed

闲来生雅趣,无事乐逍遥。对窗相望雪,一盏茶香飘。

在CMAKE中使用git命令获取版本信息

#CMake #Git @Program


基本代码

if(EXISTS "${PROJECT_SOURCE_DIR}/.git/logs/HEAD")
  find_program(GIT_PROGRAM NAMES git)
  if(${GIT_PROGRAM} STREQUAL GIT_PROGRAM-NOTFOUND)
    set(BUILD_GIT_ID "[unknown]unknown\(built from non-git or tarball\)")
  else()
    execute_process(COMMAND git -c core.fileMode=false describe --always --dirty
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    OUTPUT_VARIABLE BUILD_GIT_ID_1T)
    execute_process(COMMAND git log -1 --format=%s
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    OUTPUT_VARIABLE BUILD_GIT_ID_2T)
    execute_process(COMMAND git branch --show-current
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    OUTPUT_VARIABLE BUILD_GIT_BRANCH_T)
    string(REPLACE "\n" "" BUILD_GIT_ID_1 ${BUILD_GIT_ID_1T})
    string(REPLACE "\n" "" BUILD_GIT_ID_2 ${BUILD_GIT_ID_2T})
    string(REPLACE "\"" "\\\"" BUILD_GIT_ID_1 ${BUILD_GIT_ID_1})
    string(REPLACE "\"" "\\\"" BUILD_GIT_ID_2 ${BUILD_GIT_ID_2})
    string(REPLACE "\n" "" BUILD_GIT_BRANCH ${BUILD_GIT_BRANCH_T})
    set(BUILD_GIT_ID "[${BUILD_GIT_BRANCH}]${BUILD_GIT_ID_1}\(${BUILD_GIT_ID_2}\)")

    execute_process(COMMAND git rev-list --count HEAD
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    OUTPUT_VARIABLE BUILD_GIT_REV_CNT)
    string(REPLACE "\n" "" BUILD_GIT_REV_CNT ${BUILD_GIT_REV_CNT})
    set(NPATCH_VERSION ${BUILD_GIT_REV_CNT})
  endif()
else ()
  set(BUILD_GIT_ID "[unknown]unknown\(built from non-git or tarball\)")
endif()

if("${BUILD_GIT_ID}" MATCHES ".*dirty.*")
    string(ASCII 27 Esc)
    install(CODE "MESSAGE(\"${Esc}[31mDIRTY CODES : ${BUILD_GIT_ID}${Esc}[m\")")
endif("${BUILD_GIT_ID}" MATCHES ".*dirty.*")

这样做之后,可以得到几个有用的宏:

  • BUILD_GIT_ID_1 : 也就是git commit log的前7位。这是一个字符串。
  • BUILD_GIT_ID_2 : 也就是git commit message。这是一个字符串。
  • BUILD_GIT_REV_CNT : 也就是git commit count。这是一个数字,可以用来给so添加版本号。

同时,一旦目录内存在修改,那么在make install时,会有一行红颜色的提示。 并且编译的结果中会存在“–dirty”的标记。表示代码和git版本号并不完全相同。

连接库中带版本号

注意,将其中的“PKG”替换成你喜欢的内容。

set (NMAJOR_VERSION 1)
set (NMINOR_VERSION 0)
set (NPATCH_VERSION 0)
set (NHOTFIX_VERSION 0)

SET (PKG_VERSION ${NMAJOR_VERSION}.${NMINOR_VERSION}.${NPATCH_VERSION})
set_target_properties(PKG PROPERTIES SOVERSION ${PKG_VERSION })

让程序可以输出信息

创建一个类似下面的头文件,取名叫做config.h.in

/*****************************************************************************
 *                                                                           *
 * CAUTION : This file was generated by the cmake, never modify it manually. *
 *                                                                           *
 *****************************************************************************/

#ifndef __CONFIG_H__
#define __CONFIG_H__

#define BUILD_GIT_ID ("@BUILD_GIT_ID@")

#define PKG_VERSION ("@PKG_VERSION@")

#define PKG_VERSION_MAJOR  (@NMAJOR_VERSION@)
#define PKG_VERSION_MINOR  (@NMINOR_VERSION@)
#define PKG_VERSION_PATCH  (@NPATCH_VERSION@)
#define PKG_VERSION_HOTFIX (@NHOTFIX_VERSION@)

#define PKG_VERSION_REV ("@BUILD_GIT_ID_1@")

#define PKG_COMPILE_OPT ("@CMAKE_ARGS@")

#endif

然后在CMakeLists.txt中添加:

configure_file (
   "${PROJECT_SOURCE_DIR}/config.h.in"
   "${PROJECT_BINARY_DIR}/config.h"
)

之后,就可以在需要的源文件中包含这个头文件,并使用这些宏定义。

编译参数

更极端的情况下,我们可能还希望知道二进制生成时使用的编译参数是什么。

get_cmake_property(CACHE_VARS CACHE_VARIABLES)
foreach(CACHE_VAR ${CACHE_VARS})
  get_property(CACHE_VAR_HELPSTRING CACHE ${CACHE_VAR} PROPERTY HELPSTRING)
  #message("CACHE_VAR: ${CACHE_VAR} ${CACHE_VAR_HELPSTRING}")
  if(NOT CACHE_VAR_HELPSTRING STREQUAL "")
      string(SUBSTRING ${CACHE_VAR_HELPSTRING} 0 11 HELPSTRING_OVERHEAD)
      if(HELPSTRING_OVERHEAD STREQUAL "Build with ")
        get_property(CACHE_VAR_TYPE CACHE ${CACHE_VAR} PROPERTY TYPE)
        if(CACHE_VAR_TYPE STREQUAL "UNINITIALIZED")
          set(CACHE_VAR_TYPE)
        else()
          set(CACHE_VAR_TYPE :${CACHE_VAR_TYPE})
        endif()
        set(CMAKE_ARGS "${CMAKE_ARGS} -D${CACHE_VAR}=${${CACHE_VAR}}")
      endif()
  endif()
endforeach()
string(REPLACE "cmake . " "" CMAKE_ARGS ${CMAKE_ARGS})

使用

     (void)fprintf(stderr, "Version %d.%d.%d.%s\n",
        PKG_VERSION_MAJOR, PKG_VERSION_MINOR,
        PKG_VERSION_PATCH, PKG_VERSION_REV);
     (void)fprintf(stderr, "Compile options: %s\n\n", PKG_COMPILE_OPT);

效果

Version 1.0.134.d8ba08e
Compile options:  -DBUILD_TESTCASES=OFF -DWITH_WARN_TO_ERR=ON

在CMAKE中使用git命令获取版本信息

基本代码

if(EXISTS "${PROJECT_SOURCE_DIR}/.git/logs/HEAD")
  find_program(GIT_PROGRAM NAMES git)
  if(${GIT_PROGRAM} STREQUAL GIT_PROGRAM-NOTFOUND)
    set(BUILD_GIT_ID "[unknown]unknown\(built from non-git or tarball\)")
  else()
    execute_process(COMMAND git -c core.fileMode=false describe --always --dirty
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    OUTPUT_VARIABLE BUILD_GIT_ID_1T)
    execute_process(COMMAND git log -1 --format=%s
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    OUTPUT_VARIABLE BUILD_GIT_ID_2T)
    execute_process(COMMAND git branch --show-current
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    OUTPUT_VARIABLE BUILD_GIT_BRANCH_T)
    string(REPLACE "\n" "" BUILD_GIT_ID_1 ${BUILD_GIT_ID_1T})
    string(REPLACE "\n" "" BUILD_GIT_ID_2 ${BUILD_GIT_ID_2T})
    string(REPLACE "\"" "\\\"" BUILD_GIT_ID_1 ${BUILD_GIT_ID_1})
    string(REPLACE "\"" "\\\"" BUILD_GIT_ID_2 ${BUILD_GIT_ID_2})
    string(REPLACE "\n" "" BUILD_GIT_BRANCH ${BUILD_GIT_BRANCH_T})
    set(BUILD_GIT_ID "[${BUILD_GIT_BRANCH}]${BUILD_GIT_ID_1}\(${BUILD_GIT_ID_2}\)")

    execute_process(COMMAND git rev-list --count --first-parent HEAD
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    OUTPUT_VARIABLE BUILD_GIT_REV_CNT)
    string(REPLACE "\n" "" BUILD_GIT_REV_CNT ${BUILD_GIT_REV_CNT})
    set(NPATCH_VERSION ${BUILD_GIT_REV_CNT})
  endif()
else ()
  set(BUILD_GIT_ID "[unknown]unknown\(built from non-git or tarball\)")
endif()

if("${BUILD_GIT_ID}" MATCHES ".*dirty.*")
    string(ASCII 27 Esc)
    install(CODE "MESSAGE(\"${Esc}[31mDIRTY CODES : ${BUILD_GIT_ID}${Esc}[m\")")
endif("${BUILD_GIT_ID}" MATCHES ".*dirty.*")

这样做之后,可以得到几个有用的宏:

  • BUILD_GIT_ID_1 : 也就是git commit log的前7位。这是一个字符串。
  • BUILD_GIT_ID_2 : 也就是git commit message。这是一个字符串。
  • BUILD_GIT_REV_CNT : 也就是git commit count。这是一个数字,可以用来给so添加版本号。

同时,一旦目录内存在修改,那么在make install时,会有一行红颜色的提示。 并且编译的结果中会存在“–dirty”的标记。表示代码和git版本号并不完全相同。

连接库中带版本号

注意,将其中的“PKG”替换成你喜欢的内容。

set (NMAJOR_VERSION 1)
set (NMINOR_VERSION 0)
set (NPATCH_VERSION 0)
set (NHOTFIX_VERSION 0)

SET (PKG_VERSION ${NMAJOR_VERSION}.${NMINOR_VERSION}.${NPATCH_VERSION})
set_target_properties(PKG PROPERTIES SOVERSION ${PKG_VERSION })

编译参数

更极端的情况下,我们可能还希望知道二进制生成时使用的编译参数是什么。

get_cmake_property(CACHE_VARS CACHE_VARIABLES)
foreach(CACHE_VAR ${CACHE_VARS})
  get_property(CACHE_VAR_HELPSTRING CACHE ${CACHE_VAR} PROPERTY HELPSTRING)
  #message("CACHE_VAR: ${CACHE_VAR} ${CACHE_VAR_HELPSTRING}")
  if(NOT CACHE_VAR_HELPSTRING STREQUAL "")
      string(SUBSTRING ${CACHE_VAR_HELPSTRING} 0 11 HELPSTRING_OVERHEAD)
      if(HELPSTRING_OVERHEAD STREQUAL "Build with ")
        get_property(CACHE_VAR_TYPE CACHE ${CACHE_VAR} PROPERTY TYPE)
        if(CACHE_VAR_TYPE STREQUAL "UNINITIALIZED")
          set(CACHE_VAR_TYPE)
        else()
          set(CACHE_VAR_TYPE :${CACHE_VAR_TYPE})
        endif()
        set(CMAKE_ARGS "${CMAKE_ARGS} -D${CACHE_VAR}=${${CACHE_VAR}}")
      endif()
  endif()
endforeach()
string(REPLACE "cmake . " "" CMAKE_ARGS ${CMAKE_ARGS})

让程序可以输出信息

创建一个类似下面的头文件,取名叫做config.h.in

/*****************************************************************************
 *                                                                           *
 * CAUTION : This file was generated by the cmake, never modify it manually. *
 *                                                                           *
 *****************************************************************************/

#ifndef __CONFIG_H__
#define __CONFIG_H__

#define BUILD_GIT_ID ("@BUILD_GIT_ID@")

#define PKG_VERSION ("@PKG_VERSION@")

#define PKG_VERSION_MAJOR  (@NMAJOR_VERSION@)
#define PKG_VERSION_MINOR  (@NMINOR_VERSION@)
#define PKG_VERSION_PATCH  (@NPATCH_VERSION@)
#define PKG_VERSION_HOTFIX (@NHOTFIX_VERSION@)

#define PKG_VERSION_REV ("@BUILD_GIT_ID_1@")

#define PKG_COMPILE_OPT ("@CMAKE_ARGS@")

#endif

然后在CMakeLists.txt中添加:

configure_file (
   "${PROJECT_SOURCE_DIR}/config.h.in"
   "${PROJECT_BINARY_DIR}/config.h"
)

之后,就可以在需要的源文件中包含这个头文件,并使用这些宏定义。

使用

#include <config.h>

...

(void)fprintf(stderr, "Version %d.%d.%d.%s\n",
                      PKG_VERSION_MAJOR, PKG_VERSION_MINOR,
                      PKG_VERSION_PATCH, PKG_VERSION_REV);
(void)fprintf(stderr, "Compile options: %s\n\n", PKG_COMPILE_OPT);

...

效果示意

Version 1.0.123.d8ba08e
Compile options:  -DBUILD_TESTCASES=OFF -DWITH_WARN_TO_ERR=ON