cmake를 사용하여 git sha1을 컴파일러로 전달하려면 어떻게해야합니까?
문제
makefile에서 이것은 다음과 같은 것과 함께 이루어집니다.
g++ -DGIT_SHA1="`git log -1 | head -n 1`" ...
이진은 정확한 Commit SHA1을 알고 있으므로 Segfault의 경우 버릴 수 있기 때문에 매우 유용합니다.
CMAKE와 동일하게 달성하려면 어떻게해야합니까?
해결책
버전 및 유사한 목적을 위해 GIT 리포지토리로 피어링하는 Cmake 모듈을 만들었습니다. 모두 저의 저장소에 있습니다. https://github.com/rpavlik/cmake-modules
이러한 기능에 대한 좋은 점은 헤드가 변경 될 때마다 빌드 전에 재구성 (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)
그런 다음 시스템 전체의 정의로 추가 할 수 있습니다 (불행히도 많은 재건을 유발할 것입니다).
add_definitions("-DGIT_SHA1=${GIT_SHA1}")
또는 제안 된 대안 : 생성 된 소스 파일을 만듭니다. 소스 에서이 두 파일을 만듭니다.
gitsha1.cpp.in :
#define GIT_SHA1 "@GIT_SHA1@"
const char g_GIT_SHA1[] = GIT_SHA1;
gitsha1.h :
extern const char g_GIT_SHA1[];
이것을 당신에게 추가하십시오 CMakeLists.txt
(소스에 소스 파일 목록이 있다고 가정) :
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)
그런 다음 SHA 문자열이 포함 된 전역 변수가 있습니다. SHA가있는 경우 외부의 헤더가 변경되지 않으므로 문자열을 참조하려는 모든 장소를 포함시킬 수 있으며 생성 된 CPP 만 필요로합니다. 모든 곳에서 SHA에 액세스 할 수 있도록 모든 커밋에 다시 고합하십시오.
다른 팁
나는 생성하는 방식과 같은 방식으로 이것을했다.
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";
빌드를 수행 한 작업 공간에 보류 중이거나 커밋되지 않은 변경 사항이 있으면 위의 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)
이것은 필요합니다 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@";
그리고 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;
};
}
그런 다음 코드에서 다음을 쓸 수 있습니다.
cout << "Build SHA1: " << Version::GIT_SHA1 << endl;
나는 STH를 사용할 것입니다. 내 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}" )
저장소의 변경 사항을 포착하는 솔루션을 갖는 것이 좋을 것입니다 ( git describe --dirty
), 그러나 GIT 정보에 대한 무언가가 변경된 경우에만 재 컴파일 만 트리거합니다.
기존 솔루션 중 일부 :
- 'execute_process'를 사용하십시오. 이것은 구성 시간에 GIT 정보 만 가져 오며 저장소 변경을 놓칠 수 있습니다.
- 의지하다
.git/logs/HEAD
. 이것은 Repo의 무언가가 변경 될 때만 재 계산을 유발하지만 '-dirty'상태를 얻기 위해 변경 사항을 놓치게됩니다. - 사용자 정의 명령을 사용하여 빌드가 실행될 때마다 버전 정보를 재구성하십시오. 이것은 변화를 일으킨다
-dirty
상태이지만 항상 재 컴파일을 트리거합니다 (버전 정보 파일의 업데이트 된 타임 스탬프를 기준으로)
세 번째 솔루션의 한 가지 수정은 CMake 'Copy_IF_DIFFERENT'명령을 사용하는 것입니다. 따라서 버전 정보 파일의 타임 스탬프는 내용이 변경되는 경우에만 변경됩니다.
사용자 정의 명령의 단계는 다음과 같습니다.
- GIT 정보를 임시 파일로 수집하십시오
- 'copy_if_different'를 사용하여 임시 파일을 실제 파일로 복사하십시오.
- 다음 'make'에서 다시 실행하도록 사용자 정의 명령을 트리거하려면 임시 파일을 삭제하십시오.
코드 (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})
다음 솔루션은 GIT가 귀하를 때마다 헤드 로그를 업데이트한다는 관찰을 기반으로합니다. pull
또는 commit
무엇. 위의 Drew의 제안은 모든 후에 CMAKE 캐시를 수동으로 재건 할 때만 GIT 정보를 업데이트 할 것입니다. commit
.
나는 한 줄 헤더 파일을 생성하는 cmake "custom command"를 사용합니다. ${SRCDIR}/gitrevision.hh
어디 ${SRCDIR}
소스 트리의 근본입니다. 다시 만들어 질 것입니다 뿐 새로운 커밋이 이루어질 때. 다음은 몇 가지 의견과 함께 필요한 Cmake Magic입니다.
# 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()
의 내용 gitrevision.hh
다음과 같이 보일 것입니다.
#define GITREVISION cb93d53 2014-03-13 11:08:15 +0100
이것을 변경하려면 편집하십시오 --pretty=format:
그에 따라 사양. 예 : 사용 %H
대신에 %h
전체 SHA1 다이제스트를 인쇄합니다. 자세한 내용은 GIT 매뉴얼을 참조하십시오.
만들기 gitrevision.hh
가드 등이 포함 된 본격적인 C ++ 헤더 파일은 독자에게 연습으로 남겨 둡니다 :-)
I can't help you with the CMake side, but with respect to Git side I would recommend taking a look how Linux kernel and Git project itself does it, via GIT-VERSION-GEN script, or how tig does it in its Makefile, by using git describe
if there is git repository present, falling back to "version
" / "VERSION
" / "GIT-VERSION-FILE
" generated and present in tarballs, finally falling back to default value hardcoded in script (or Makefile).
The first part (using git describe
) requires that you tag releases using annotated (and possibly GPG signed) tags. Or use git describe --tags
to use also lightweight tags.
여기 내 솔루션이 있습니다. 합리적으로 짧지 만 효과적이라고 생각합니다. ;-)
먼저 소스 트리에 파일이 필요합니다 (이름을 지정합니다. git-rev.h.in
), 그것은 다음과 같이 보일 것입니다.
#define STR_EXPAND(x) #x
#define STR(x) STR_EXPAND(x)
#define GIT_REV STR(GIT_REV_)
#define GIT_REV_ \
(그 매크로를 신경 쓰지 마십시오. 그것은 원시 가치로 끈을 만들기위한 약간 미친 트릭입니다.)이 파일은 가지고 있어야합니다. 바로 그거죠 값을 추가 할 수 있도록 끝에 하나의 빈 신성 라인.
그리고 이제이 코드는 각자입니다 CMakeLists.txt
파일:
# --- Git revision ---
add_dependencies(your_awesome_target gitrev) #put name of your target here
include_directories(${CMAKE_CURRENT_BINARY_DIR}) #so that the include file is found
set(gitrev_in git-rev.h.in) #just filenames, feel free to change them...
set(gitrev git-rev.h)
add_custom_target(gitrev
${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/${gitrev}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${gitrev_in} ${CMAKE_CURRENT_BINARY_DIR}/${gitrev}
COMMAND git rev-parse HEAD >> ${CMAKE_CURRENT_BINARY_DIR}/${gitrev}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} #very important, otherwise git repo might not be found in shadow build
VERBATIM #portability wanted
)
이 명령은 git-rev.h.in
빌드 트리에 복사됩니다 git-rev.h
그리고 GIT 개정은 끝에 추가되었습니다.
따라서 다음에해야 할 일은 포함됩니다 git-rev.h
파일 중 하나에서 원하는대로 GIT_REV
MACRO는 현재 GIT 개정 해시를 문자열 값으로 생성합니다.
이 솔루션의 좋은 점은 git-rev.h
관련 대상을 구축 할 때마다 재현되므로 실행할 필요가 없습니다. cmake
다시 반복하여.
또한 휴대하기 쉬워야합니다. 포트할 수없는 외부 도구가 사용되지 않았으며 피의 바보 같은 Windows CMD조차도 지원합니다. >
그리고 >>
운영자 ;-)
해결책
두 파일에만 코드를 추가하기 만하면됩니다. CMakeList.txt
그리고 main.cpp
.
1. cmakelist.txt
# 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}\"")
2. main.cpp
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
}
설명
~ 안에 CMakeList.txt
, cmake 명령execute_process()
명령을 호출하는 데 사용됩니다 git log -1 --format=%h
그것은 당신에게 sha-1 값에 대한 짧고 독특한 약어를 문자열로 제공합니다. 4f34ee8
. 이 문자열은 호출 된 cmake 변수에 할당됩니다 GIT_COMMIT_HASH
. cmake 명령 add_definitions()
매크로를 정의합니다 GIT_COMMIT_HASH
의 가치에 4f34ee8
GCC 컴파일 직전. 해시 값은 전처리 기의 C ++ 코드의 매크로를 대체하는 데 사용되므로 객체 파일에 존재합니다. main.o
그리고 편집 된 바이너리에서 a.out
.
참고 사항
달성하는 또 다른 방법은 cmake 명령을 사용하는 것입니다. configure_file()
, 그러나 나는 cmake가 실행되기 전에 파일이 존재하지 않기 때문에 그것을 사용하는 것을 좋아하지 않습니다.
CMAKE 가이 대체를 수행 할 수있는 내장 기능이 없다면 템플릿 파일을 읽는 래퍼 쉘 스크립트를 작성하여 올바른 위치에서 위와 같이 SHA1 해시를 대체 할 수 있습니다. sed
, 예를 들어), 실제 CMAKE 빌드 파일을 생성 한 다음 CMAKE를 호출하여 프로젝트를 구축합니다.
약간 다른 접근법은 SHA1 대체를하는 것일 수 있습니다. 선택 과목. 당신은 다음과 같은 더미 해시 값으로 cmake 파일을 만듭니다. "NO_OFFICIAL_SHA1_HASH"
. 개발자가 작업 디렉토리에서 자체 빌드를 구축 할 때 빌드 코드에는 작업 디렉토리의 코드에 해당 SHA1 해시 값이 없기 때문에 SHA1 해시 값 (더미 값 만)이 포함되지 않습니다.
반면에, 빌드 서버가 공식 빌드를 만들면 중앙 저장소에서 가져온 소스에서 소스 코드의 SHA1 해시 값을 알 수 있습니다. 이 시점에서 CMAKE 파일에서 해시 값을 대체 한 다음 CMAKE를 실행할 수 있습니다.
빠르고 더럽고, git sha-1을 cmake를 사용하여 c 또는 c ++ 프로젝트로 가져 오는 휴대용 방법이 아닐 수도 있습니다. cmakelists.txt : txt : txt :
add_custom_target(git_revision.h
git log -1 "--format=format:#define GIT_REVISION \"%H\"%n" HEAD > git_revision.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} VERBATIM)
그것은 그것을 가정합니다 CMAKE_SOURCE_DIR
git 저장소의 일부이며, 그 git은 시스템에서 사용할 수 있으며 출력 리디렉션은 쉘에 의해 제대로 구문 분석됩니다.
그런 다음이 목표를 사용하여 다른 대상의 종속성으로 만들 수 있습니다.
add_dependencies(your_program git_revision.h)
매번 your_program
제작 된 Makefile (또는 다른 빌드 시스템에서 작동하는 경우 기타 빌드 시스템)은 컨텐츠와 함께 소스 디렉토리에서 git_revision.h를 재현합니다.
#define GIT_REVISION "<SHA-1 of the current git revision>"
그래서 당신은 할 수 있습니다 #include git_revision.h
일부 소스 코드 파일에서 그런 식으로 사용하십시오. 헤더는 문자 그대로 생성됩니다 모든 빌드, 즉 다른 모든 객체 파일이 최신 상태이더라도 git_revision.h를 다시 만들기 위해이 명령을 실행합니다. 나는 일반적으로 당신이 같은 git 개정을 계속해서 반복해서 재건하지 않기 때문에 큰 문제가되어서는 안된다고 생각합니다. ~이다 당신을위한 문제, 이것을 사용하지 마십시오. (아마도 해결 방법을 해킹 할 수 있습니다. add_custom_command
그러나 나는 지금까지 그것을 필요로하지 않았다.)