Comment puis-je obtenir mon code C pour imprimer automatiquement son hash version Git?

Existe-t-il un moyen facile d’écrire du code C pouvant accéder à sa version Git?

J’ai écrit un logiciel en C pour collecter des données scientifiques en laboratoire. Mon code enregistre les données qu’il collecte dans un fichier .yaml pour une parsing ultérieure. Mes expériences changent au jour le jour et je dois souvent modifier le code. Pour garder une trace des révisions, j’utilise un repository git.

Je voudrais pouvoir inclure le hash de révision Git comme commentaire dans mes fichiers de données .yaml. De cette façon, je pourrais regarder le fichier .yaml et savoir exactement quel code a été utilisé pour générer les données affichées dans ce fichier. Existe-t-il un moyen facile de le faire automatiquement?

Dans mon programme, je détiens le numéro de version de git et la date de la construction dans un fichier séparé, appelé version.c , qui ressemble à ceci:

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

Il y a aussi un fichier d’en-tête qui ressemble à ceci:

 #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 */ 

Le fichier d’en-tête et le fichier C sont tous deux générés par un script Perl qui ressemble à ceci:

 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_"); 

Ici hash_to_c_file fait tout le travail de création de version.c et version.h et make_date_time crée une chaîne comme indiqué.

Dans le programme principal, j’ai une routine

 #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); } 

Je ne connais pas bien Git, donc je serais ravi de recevoir des commentaires s’il existe une meilleure façon de le faire.

Si vous utilisez une version basée sur make, vous pouvez la placer dans le Makefile:

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

(Voir man git décrire ce que font les commutateurs)

puis ajoutez ceci à votre CFLAGS:

 -DVERSION=\"$(GIT_VERSION)\" 

Ensuite, vous pouvez simplement référencer la version directement dans le programme comme si c’était un #define:

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

Par défaut, ceci imprime simplement un identifiant de validation abrégé git, mais vous pouvez éventuellement marquer des versions particulières avec quelque chose comme:

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

alors il imprimera:

 Version: v1.1-2-g766d 

ce qui signifie que 2 commet passé v1.1, avec un identifiant de validation git commençant par “766d”.

S’il y a des modifications non validées dans votre arbre, il appenda “-dirty”.

Il n’y a pas d’parsing des dépendances, vous devez donc faire un make clean explicite pour forcer la mise à jour de la version. Cela peut être résolu cependant.

Les avantages sont que c’est simple et ne nécessite aucune dépendance de construction supplémentaire comme perl ou awk. J’ai utilisé cette approche avec GNU automake et avec les versions Android NDK.

J’ai fini par utiliser quelque chose de très similaire à la réponse de @ Kinopiko, mais j’ai utilisé awk au lieu de perl. Ceci est utile si vous êtes bloqué sur les machines Windows qui ont awk installé par nature de mingw, mais pas perl. Voici comment cela fonctionne.

Mon makefile contient une ligne qui appelle git, date et awk pour créer un fichier 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 

Chaque fois que je comstack mon code, la commande awk génère un fichier version.c qui ressemble à ceci:

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

J’ai un fichier version.h statique qui ressemble à ceci:

 /*version.h*/ #ifndef VERSION_H_ #define VERSION_H_ extern const char * build_git_time; extern const char * build_git_sha; #endif /* VERSION_H_ */ 

Le rest de mon code peut maintenant accéder à la compilation et au hachage git en incluant simplement l’en-tête version.h. Pour terminer, je dis à git d’ignorer version.c en ajoutant une ligne à mon fichier .gitignore. De cette façon, git ne me donne pas constamment des conflits de fusion. J’espère que cela t’aides!

Votre programme peut être git describe pour git describe , à l’exécution ou dans le cadre du processus de génération.

Il y a deux choses que vous pouvez faire:

  • Vous pouvez faire en sorte que Git intègre des informations de version dans le fichier pour vous.

    Le moyen le plus simple est d’utiliser l’ atsortingbut ident , qui signifie mettre (par exemple)

     *.yaml ident 

    dans le fichier .gitatsortingbutes et $Id$ à la place appropriée. Il serait automatiquement étendu à l’identifiant SHA-1 du contenu du fichier (identifiant de l’object blob): il ne s’agit PAS de la version du fichier ou de la dernière validation.

    Git supporte $ Id $ keyword de cette façon pour éviter de toucher des fichiers qui n’ont pas été modifiés lors du changement de twig, du rembobinage, etc. Si vous voulez vraiment que Git mette un identifiant ou une description de validation dans le fichier, vous pouvez (ab) utiliser filter atsortingbut filter , en utilisant le filter clean / smudge pour développer un mot clé (par exemple $ Revision $) lors de la validation, et le nettoyer pour un commit.

  • Vous pouvez faire en sorte que le processus de compilation le fasse pour vous, comme le kernel Linux ou Git lui-même.

    Jetez un coup d’oeil au script GIT-VERSION-GEN et à son utilisation dans Git Makefile , ou par exemple comment ce Makefile incorpore les informations de version lors de la génération / configuration du fichier gitweb/gitweb.cgi .

    GIT-VERSION-GEN utilise git describe pour générer la description de la version. Il doit mieux fonctionner que vous marquez (en utilisant des balises signées / annotées) les versions / jalons de votre projet.

Lorsque je dois le faire, j’utilise un tag , comme RELEASE_1_23 . Je peux décider de ce que le tag peut être sans connaître le SHA-1. Je commets alors un tag. Vous pouvez stocker cette balise dans votre programme comme vous le souhaitez.

En me basant sur la réponse donnée par njd27, j’utilise une version avec parsing des dépendances, en combinaison avec un fichier version.h avec des valeurs par défaut lorsque le code est construit différemment. Tous les fichiers qui incluent version.h seront reconstruits.

Il inclut également la date de révision en tant que définition distincte.

 # 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)) # recomstack 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) 

J’utilise aussi git pour suivre les changements dans mon code scientifique. Je ne voulais pas utiliser un programme externe car cela limite la portabilité du code (si quelqu’un souhaite apporter des modifications sur MSVS par exemple).

Ma solution consistait à utiliser uniquement la twig principale pour les calculs et à la faire sortir le temps de compilation en utilisant les macros de préprocesseur __DATE__ et __TIME__ . De cette façon, je peux le vérifier avec git log et voir quelle version j’utilise. ref: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

Une autre façon élégante de résoudre le problème consiste à inclure git log dans l’exécutable. créer un fichier object en dehors du journal git et l’inclure dans le code. Cette fois-ci, le seul programme externe que vous utilisez est objcopy mais il y a moins de codage. ref: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 et intégrer des données dans un programme C ++

Ce que vous devez faire est de générer un fichier d’en-tête (par exemple, en utilisant echo from cmd line) comme ceci:

 #define GIT_HASH \ "098709a0b098c098d0e" 

Pour le générer, utilisez quelque chose comme ceci:

 echo #define GIT_HASH \ > file.h echo " > file.h echo git status  > file.h echo " > file.h 

Peut-être aurez-vous besoin de jouer un peu avec les guillemets et les barres obliques inverses pour la comstackr, mais vous avez l’idée.

Encore une autre variante basée sur Makefile et 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) 

Le fichier git_commit_filename.h se retrouvera avec une seule ligne contenant static const char * GIT_COMMIT_SHA = “”;

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

Vous pouvez voir comment je l’ai fait pour memcached dans le commit original .

Fondamentalement, étiquetez de temps en temps, et assurez-vous que la chose que vous livrez vient de make dist ou similaire.