cmake를 사용하여 git sha1을 컴파일러로 전달하려면 어떻게해야합니까?

StackOverflow https://stackoverflow.com/questions/1435953

  •  07-07-2019
  •  | 
  •  

문제

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 정보에 대한 무언가가 변경된 경우에만 재 컴파일 만 트리거합니다.

기존 솔루션 중 일부 :

  1. 'execute_process'를 사용하십시오. 이것은 구성 시간에 GIT 정보 만 가져 오며 저장소 변경을 놓칠 수 있습니다.
  2. 의지하다 .git/logs/HEAD. 이것은 Repo의 무언가가 변경 될 때만 재 계산을 유발하지만 '-dirty'상태를 얻기 위해 변경 사항을 놓치게됩니다.
  3. 사용자 정의 명령을 사용하여 빌드가 실행될 때마다 버전 정보를 재구성하십시오. 이것은 변화를 일으킨다 -dirty 상태이지만 항상 재 컴파일을 트리거합니다 (버전 정보 파일의 업데이트 된 타임 스탬프를 기준으로)

세 번째 솔루션의 한 가지 수정은 CMake 'Copy_IF_DIFFERENT'명령을 사용하는 것입니다. 따라서 버전 정보 파일의 타임 스탬프는 내용이 변경되는 경우에만 변경됩니다.

사용자 정의 명령의 단계는 다음과 같습니다.

  1. GIT 정보를 임시 파일로 수집하십시오
  2. 'copy_if_different'를 사용하여 임시 파일을 실제 파일로 복사하십시오.
  3. 다음 '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 그러나 나는 지금까지 그것을 필요로하지 않았다.)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top