Program Tip

Git 버전 해시를 자동으로 인쇄하도록 C 코드를 얻으려면 어떻게해야합니까?

programtip 2020. 10. 15. 21:28
반응형

Git 버전 해시를 자동으로 인쇄하도록 C 코드를 얻으려면 어떻게해야합니까?


Git 버전 해시에 액세스 할 수있는 C 코드를 작성하는 쉬운 방법이 있습니까?

실험실 환경에서 과학 데이터를 수집하기 위해 C로 소프트웨어를 작성했습니다. 내 코드는 수집 한 데이터를 나중에 분석하기 위해 .yaml 파일에 기록합니다. 내 실험은 매일 바뀌고 종종 코드를 수정해야합니다. 개정을 추적하기 위해 git 저장소를 사용합니다.

내 .yaml 데이터 파일에 주석으로 Git 개정 해시를 포함하고 싶습니다. 이렇게하면 .yaml 파일을보고 해당 파일에 표시된 데이터를 생성하는 데 사용 된 코드를 정확히 알 수 있습니다. 이 작업을 자동으로 수행하는 쉬운 방법이 있습니까?


내 프로그램에서 git 버전 번호와 빌드 날짜를라는 별도의 파일에 보관 version.c합니다.

#include "version.h"
const char * build_date = "2009-11-10 11:09";
const char * build_git_sha = "6b54ea36e92d4907aba8b3fade7f2d58a921b6cd";

다음과 같은 헤더 파일도 있습니다.

#ifndef VERSION_H
#define VERSION_H
extern const char * build_date; /* 2009-11-10 11:09 */
extern const char * build_git_sha; /* 6b54ea36e92d4907aba8b3fade7f2d58a921b6cd */
#endif /* VERSION_H */

헤더 파일과 C 파일은 모두 다음과 같은 Perl 스크립트에 의해 생성됩니다.

my $git_sha = `git rev-parse HEAD`;
$git_sha =~ s/\s+//g;
# This contains all the build variables.
my %build;
$build{date} = make_date_time ();
$build{git_sha} = $git_sha;

hash_to_c_file ("version.c", \%build, "build_");

여기에 hash_to_c_file만드는 모든 작업 수행 version.cversion.hmake_date_time같이 문자열을 만든다.

메인 프로그램에는 루틴이 있습니다.

#include "version.h"

// The name of this program.
const char * program_name = "magikruiser";
// The version of this program.
const char * version = "0.010";

/* Print an ID stamp for the program. */

static void _program_id_stamp (FILE * output)
{
    fprintf (output, "%s / %s / %s / %s\n",
             program_name, version,
             build_date, build_git_sha);
}

나는 git에 대해 잘 알지 못하기 때문에 더 나은 방법이 있다면 의견을 환영합니다.


make 기반 빌드를 사용하는 경우 Makefile에 넣을 수 있습니다.

GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"

( 스위치의 기능 man git describe 참조 )

그런 다음 CFLAGS에 다음을 추가하십시오.

-DVERSION=\"$(GIT_VERSION)\"

그런 다음 #define처럼 프로그램에서 직접 버전을 참조 할 수 있습니다.

printf("Version: %s\n", VERSION);

기본적으로 이것은 축약 된 git commit id를 인쇄하지만 선택적으로 특정 릴리스에 다음과 같은 태그를 지정할 수 있습니다.

git tag -a v1.1 -m "Release v1.1"

그러면 다음과 같이 인쇄됩니다.

Version: v1.1-2-g766d

즉, "766d"로 시작하는 git commit id로 v1.1 이전에 2 번 커밋됩니다.

트리에 커밋되지 않은 변경 사항이 있으면 "-dirty"를 추가합니다.

종속성 검사가 없으므로 make clean버전을 강제로 업데이트 하려면 명시 적으로 수행해야합니다 . 그러나 이것은 해결할 수 있습니다 .

장점은 간단하고 perl 또는 awk와 같은 추가 빌드 종속성이 필요하지 않다는 것입니다. GNU automake 및 Android NDK 빌드에서이 접근 방식을 사용했습니다.


@Kinopiko의 답변과 매우 유사한 것을 사용했지만 perl 대신 awk를 사용했습니다. 이것은 mingw의 특성상 awk가 설치되어 있지만 perl이 아닌 Windows 시스템에 붙어있는 경우 유용합니다. 작동 원리는 다음과 같습니다.

내 makefile에는 git, date 및 awk를 호출하여 ac 파일을 만드는 줄이 있습니다.

$(MyLibs)/version.c: FORCE 
    $(GIT) rev-parse HEAD | awk ' BEGIN {print "#include \"version.h\""} {print "const char * build_git_sha = \"" $$0"\";"} END {}' > $(MyLibs)/version.c
    date | awk 'BEGIN {} {print "const char * build_git_time = \""$$0"\";"} END {} ' >> $(MyLibs)/version.c 

코드를 컴파일 할 때마다 awk 명령은 다음과 같은 version.c 파일을 생성합니다.

/* version.c */
#include "version.h"
const char * build_git_sha = "ac5bffc90f0034df9e091a7b3aa12d150df26a0e";
const char * build_git_time = "Thu Dec  3 18:03:58 EST 2009";

다음과 같은 정적 version.h 파일이 있습니다.

/*version.h*/
#ifndef VERSION_H_
#define VERSION_H_

extern const char * build_git_time;
extern const char * build_git_sha;


#endif /* VERSION_H_ */

이제 나머지 코드는 version.h 헤더를 포함하여 빌드 시간과 git 해시에 액세스 할 수 있습니다. 모든 것을 마무리하기 위해 .gitignore 파일에 한 줄을 추가하여 git에게 version.c를 무시하도록 지시합니다. 이런 식으로 git은 지속적으로 병합 충돌을 일으키지 않습니다. 도움이 되었기를 바랍니다!


프로그램은 git describe런타임에 또는 빌드 프로세스의 일부로으로 쉘 아웃 할 수 있습니다 .


다음 두 가지를 수행 할 수 있습니다.

  • Git 이 파일에 일부 버전 정보를 포함 하도록 만들 수 있습니다 .

    간단한 방법은 사용하는 것입니다 ident 속성 (예를 들어) 퍼팅 의미,

    *.yaml    ident
    

    에서 .gitattributes파일 및 $Id$적절한 장소이다. 파일 내용의 SHA-1 식별자 (blob id) 로 자동 확장됩니다 . 이것은 파일 버전이 아니거나 마지막 커밋입니다.

    Git은 이러한 방식으로 $ Id $ 키워드를 지원하여 분기 전환, 분기 되감기 등에서 변경되지 않은 파일을 건드리지 않도록합니다. 정말로 Git이 파일에 커밋 (버전) 식별자 또는 설명을 입력하도록하려면 (남용) 사용할 수 있습니다. filter결제시 일부 키워드 (예 : $ Revision $)를 확장하기 위해 clean / smudge filter를 사용하고 커밋을 위해 정리합니다.

  • Linux 커널이나 Git 자체처럼 빌드 프로세스만들 수 있습니다 .

    Take a look at GIT-VERSION-GEN script and its use in Git Makefile, or for example how this Makefile embeds version information during generation / configuration of gitweb/gitweb.cgi file.

    GIT-VERSION-GEN uses git describe to generate version description. It needs to work better that you tag (using signed / annotated tags) releases / milestones of your project.


When I need to do this, I use a tag, like RELEASE_1_23. I can decide what the tag can be without knowing the SHA-1. I commit then tag. You can store that tag in your program anyway that you like.


Based on the answer by njd27, I'm using the a version with dependency scanning, in combination with a version.h file with default values for when the code is built in a different way. All files that include version.h will be rebuilt.

It also includes the revision date as a separate define.

# Get git commit version and date
GIT_VERSION := $(shell git --no-pager describe --tags --always --dirty)
GIT_DATE := $(firstword $(shell git --no-pager show --date=short --format="%ad" --name-only))

# recompile version.h dependants when GIT_VERSION changes, uses temporary file version~
.PHONY: force
version~: force
    @echo '$(GIT_VERSION) $(GIT_DATE)' | cmp -s - $@ || echo '$(GIT_VERSION) $(GIT_DATE)' > $@
version.h: version~
    @touch $@
    @echo Git version $(GIT_VERSION) $(GIT_DATE)

I also use git to track changes in my scientific code. i didn't want to use an external program because it limits portability of the code (if someone would want to make changes on MSVS for example).

my solution was to use only the main branch for the calculations and make it output the build time using preprocessor macros __DATE__ and __TIME__. that way i can check it with git log and see which version i'm using. ref: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

another elegant way to solve the problem is to include git log into the executable. make an object file out of git log and include it into the code. this time the only external program you use is objcopy but there is less coding. ref: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 and Embed data in a C++ program


What you need to do is to generate a header file (eg using echo from cmd line) something like this:

#define GIT_HASH \
"098709a0b098c098d0e"

To generate it use something like this:

echo #define GIT_HASH \ > file.h
echo " > file.h
echo git status <whatever cmd to get the hash> > file.h
echo " > file.h

Might need to play with the quotes and backslashes a bit to get it to compile, but you get the idea.


Yet another variation based on Makefile and shell

GIT_COMMIT_FILE=git_commit_filename.h

$(GIT_COMMIT_FILE): phony
    $(eval GIT_COMMIT_SHA=$(shell git describe --abbrev=6 --always 2>/dev/null || echo 'Error'))
    @echo SHA=$(GIT_COMMIT_SHA)
    echo -n "static const char *GIT_COMMIT_SHA = \"$(GIT_COMMIT_SHA)\";" > $(GIT_COMMIT_FILE)

File git_commit_filename.h will end up with a single line containing static const char *GIT_COMMIT_SHA="";

From https://gist.github.com/larytet/898ec8814dd6b3ceee65532a9916d406


You can see how I did it for memcached in the original commit.

Basically, tag occasionally, and make sure the thing you deliver comes from make dist or similar.


This is a solution for CMake project that works for Windows and Linux, without the need for any other programs (e.g. script languages) to be installed.

The git hash is written to a .h file by a script, which is a bash script when compiling on Linux or a Windows batch script when compiling on Windows, and an if-clause in CMakeLists.txt picks the script corresponding to the platform the code is compile on.

The following 2 scripts are saved in the same directory as CMakeLists.txt:

get_git_hash.sh:

#!/bin/bash
hash=$(git describe --dirty --always --tags)
echo "#ifndef GITHASH_H" > include/my_project/githash.h
echo "#define GITHASH_H" >> include/my_project/githash.h
echo "const std::string kGitHash = \"$hash\";" >> include/my_project/githash.h
echo "#endif // GITHASH_H" >> include/my_project/githash.h

get_git_hash.cmd:

@echo off
FOR /F "tokens=* USEBACKQ" %%F IN (`git describe --dirty --always --tags`) DO (
SET var=%%F
)
ECHO #ifndef GITHASH_H > include/my_project/githash.h
ECHO #define GITHASH_H >> include/my_project/githash.h
ECHO const std::string kGitHash = "%var%"; >> include/my_project/githash.h
ECHO #endif // GITHASH_H >> include/my_project/githash.h

In CMakeLists.txt the follwoing lines are added

if(WIN32)
  add_custom_target(
    run ALL
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    COMMAND get_git_hash.cmd
  )
else()
  add_custom_target(
    run ALL
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    COMMAND ./get_git_hash.sh
  )
endif()

include_directories(include)

코드에서 생성 된 파일은에 포함되며 #include <my_project/githash.h>git 해시 std::cout << "Software version: " << kGitHash << std::endl;는를 사용 하여 터미널에 인쇄 되거나 유사한 방식으로 yaml (또는 임의의) 파일에 기록 될 수 있습니다 .

참고 URL : https://stackoverflow.com/questions/1704907/how-can-i-get-my-c-code-to-automatically-print-out-its-git-version-hash

반응형