在Makefile中,这可以通过以下方式完成:
g++ -DGIT_SHA1="`git log -1 | head -n 1`" ...
Run Code Online (Sandbox Code Playgroud)
这非常有用,因为二进制文件知道确切的提交SHA1,所以它可以在segfault的情况下转储它.
我如何用CMake实现同样的目标?
Rya*_*lik 95
我已经制作了一些CMake模块,这些模块可以用于版本控制和类似用途的git仓库 - 它们都在我的存储库中,位于https://github.com/rpavlik/cmake-modules
关于这些函数的好处是,每次HEAD提交更改时,它们都会在构建之前强制重新配置(重新运行cmake).与使用execute_process执行一次操作不同,您不需要记住重新编写以更新哈希定义.
出于这个特定目的,您至少需要GetGitRevisionDescription.cmake和GetGitRevisionDescription.cmake.in文件.然后,在你的主CMakeLists.txt文件中,你会有这样的东西
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/whereYouPutMyModules/")
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
Run Code Online (Sandbox Code Playgroud)
然后,您可以将其添加为系统范围的定义(不幸的是会导致大量重建)
add_definitions("-DGIT_SHA1=${GIT_SHA1}")
Run Code Online (Sandbox Code Playgroud)
或者,我建议的替代方案:制作生成的源文件.在源中创建这两个文件:
GitSHA1.cpp.in:
#define GIT_SHA1 "@GIT_SHA1@"
const char g_GIT_SHA1[] = GIT_SHA1;
Run Code Online (Sandbox Code Playgroud)
GitSHA1.h:
extern const char g_GIT_SHA1[];
Run Code Online (Sandbox Code Playgroud)
将此添加到您的CMakeLists.txt(假设您有SOURCES中的源文件列表):
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/GitSHA1.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/GitSHA1.cpp" @ONLY)
list(APPEND SOURCES "${CMAKE_CURRENT_BINARY_DIR}/GitSHA1.cpp" GitSHA1.h)
Run Code Online (Sandbox Code Playgroud)
然后,你有一个包含SHA字符串的全局变量 - 当SHA执行时,带有extern的标题不会改变,所以你可以只包括你要引用字符串的任何地方,然后只需要生成的CPP需要在每次提交时重新编译,以便您可以访问所有SHA.
Dre*_*kes 17
我这样做是为了生成:
const std::string Version::GIT_SHA1 = "e7fb69fb8ee93ac66f006406781138562d0250fb";
const std::string Version::GIT_DATE = "Thu Jan 9 14:17:56 2014";
const std::string Version::GIT_COMMIT_SUBJECT = "Fix all the bugs";
Run Code Online (Sandbox Code Playgroud)
如果执行构建的工作空间具有挂起的未提交更改,则上面的SHA1字符串将以后缀为后缀-dirty.
在CMakeLists.txt:
# the commit's SHA1, and whether the building workspace was dirty or not
execute_process(COMMAND
"${GIT_EXECUTABLE}" describe --match=NeVeRmAtCh --always --abbrev=40 --dirty
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_SHA1
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# the date of the commit
execute_process(COMMAND
"${GIT_EXECUTABLE}" log -1 --format=%ad --date=local
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_DATE
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# the subject of the commit
execute_process(COMMAND
"${GIT_EXECUTABLE}" log -1 --format=%s
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_COMMIT_SUBJECT
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# generate version.cc
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.cc.in" "${CMAKE_CURRENT_BINARY_DIR}/version.cc" @ONLY)
list(APPEND SOURCES "${CMAKE_CURRENT_BINARY_DIR}/version.cc" version.hh)
Run Code Online (Sandbox Code Playgroud)
这需要version.cc.in:
#include "version.hh"
using namespace my_app;
const std::string Version::GIT_SHA1 = "@GIT_SHA1@";
const std::string Version::GIT_DATE = "@GIT_DATE@";
const std::string Version::GIT_COMMIT_SUBJECT = "@GIT_COMMIT_SUBJECT@";
Run Code Online (Sandbox Code Playgroud)
而且version.hh:
#pragma once
#include <string>
namespace my_app
{
struct Version
{
static const std::string GIT_SHA1;
static const std::string GIT_DATE;
static const std::string GIT_COMMIT_SUBJECT;
};
}
Run Code Online (Sandbox Code Playgroud)
然后在代码中我可以写:
cout << "Build SHA1: " << Version::GIT_SHA1 << endl;
Run Code Online (Sandbox Code Playgroud)
我会用的.在我的CMakeLists.txt中这样:
exec_program(
"git"
${CMAKE_CURRENT_SOURCE_DIR}
ARGS "describe"
OUTPUT_VARIABLE VERSION )
string( REGEX MATCH "-g.*$" VERSION_SHA1 ${VERSION} )
string( REGEX REPLACE "[-g]" "" VERSION_SHA1 ${VERSION_SHA1} )
add_definitions( -DGIT_SHA1="${VERSION_SHA1}" )
Run Code Online (Sandbox Code Playgroud)
只需将一些代码添加到仅 2 个文件中:CMakeList.txt和main.cpp.
# git commit hash macro
execute_process(
COMMAND git log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions("-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"")
Run Code Online (Sandbox Code Playgroud)
inline void LogGitCommitHash() {
#ifndef GIT_COMMIT_HASH
#define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized
#endif
std::cout << "GIT_COMMIT_HASH[" << GIT_COMMIT_HASH << "]"; // 4f34ee8
}
Run Code Online (Sandbox Code Playgroud)
在 中CMakeList.txt,CMake 命令execute_process()用于调用命令git log -1 --format=%h,该命令为您提供字符串中 SHA-1 值的简短且唯一的缩写,例如4f34ee8. 该字符串被分配给名为GIT_COMMIT_HASH. CMake 命令add_definitions()将宏定义为gcc 编译之前GIT_COMMIT_HASH的值4f34ee8。哈希值用于通过预处理器替换 C++ 代码中的宏,因此存在于目标文件main.o和编译后的二进制文件中a.out。
另一种实现方法是使用名为 的 CMake 命令configure_file(),但我不喜欢使用它,因为在运行 CMake 之前该文件不存在。
小智 7
如果有一个解决方案可以捕获存储库(来自git describe --dirty)的更改,那将是很好的,但只有在有关git信息的某些内容发生更改时才会触发重新编译.
一些现有的解决方案:
execute_process.这只会在repo中的某些内容发生更改时触发重新编译,但会错过更改以获得"-dirty"状态..git/logs/HEAD状态的更改,但会一直触发重新编译(基于版本信息文件的更新时间戳)第三种解决方案的一个修复是使用CMake'copy_if_different'命令,因此版本信息文件上的时间戳仅在内容更改时才会更改.
自定义命令中的步骤是:
代码(从kralyk的解决方案中大量借用):
# The 'real' git information file
SET(GITREV_BARE_FILE git-rev.h)
# The temporary git information file
SET(GITREV_BARE_TMP git-rev-tmp.h)
SET(GITREV_FILE ${CMAKE_BINARY_DIR}/${GITREV_BARE_FILE})
SET(GITREV_TMP ${CMAKE_BINARY_DIR}/${GITREV_BARE_TMP})
ADD_CUSTOM_COMMAND(
OUTPUT ${GITREV_TMP}
COMMAND ${CMAKE_COMMAND} -E echo_append "#define GIT_BRANCH_RAW " > ${GITREV_TMP}
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD >> ${GITREV_TMP}
COMMAND ${CMAKE_COMMAND} -E echo_append "#define GIT_HASH_RAW " >> ${GITREV_TMP}
COMMAND ${GIT_EXECUTABLE} describe --always --dirty --abbrev=40 --match="NoTagWithThisName" >> ${GITREV_TMP}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GITREV_TMP} ${GITREV_FILE}
COMMAND ${CMAKE_COMMAND} -E remove ${GITREV_TMP}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
VERBATIM
)
# Finally, the temporary file should be added as a dependency to the target
ADD_EXECUTABLE(test source.cpp ${GITREV_TMP})
Run Code Online (Sandbox Code Playgroud)
下面的解决方案是基于Git的更新日志HEAD当你观察pull或commit东西.请注意,例如,只有在每次手动重建CMake缓存后,上面的Drew建议才会更新Git信息commit.
我使用CMake"自定义命令"生成一行头文件${SRCDIR}/gitrevision.hh,其中${SRCDIR}是源树的根.只有在提交新提交时才会重新创建它.这是必要的CMake魔术和一些评论:
# Generate gitrevision.hh if Git is available
# and the .git directory is present
# this is the case when the software is checked out from a Git repo
find_program(GIT_SCM git DOC "Git version control")
mark_as_advanced(GIT_SCM)
find_file(GITDIR NAMES .git PATHS ${CMAKE_SOURCE_DIR} NO_DEFAULT_PATH)
if (GIT_SCM AND GITDIR)
# Create gitrevision.hh
# that depends on the Git HEAD log
add_custom_command(OUTPUT ${SRCDIR}/gitrevision.hh
COMMAND ${CMAKE_COMMAND} -E echo_append "#define GITREVISION " > ${SRCDIR}/gitrevision.hh
COMMAND ${GIT_SCM} log -1 "--pretty=format:%h %ai" >> ${SRCDIR}/gitrevision.hh
DEPENDS ${GITDIR}/logs/HEAD
VERBATIM
)
else()
# No version control
# e.g. when the software is built from a source tarball
# and gitrevision.hh is packaged with it but no Git is available
message(STATUS "Will not remake ${SRCDIR}/gitrevision.hh")
endif()
Run Code Online (Sandbox Code Playgroud)
内容gitrevision.hh将如下所示:
#define GITREVISION cb93d53 2014-03-13 11:08:15 +0100
Run Code Online (Sandbox Code Playgroud)
如果要更改此值,请相应地编辑--pretty=format:规范.例如,使用%H而不是%h将打印完整的SHA1摘要.有关详细信息,请参阅Git手册.
制作gitrevision.hh一个包含防护等功能的完全成熟的C++头文件留给读者练习:-)