Voulez-vous utiliser _ DATE __ ET __ TIME _ en tant qu'entier pour donner une version automatisée à mon code lors de la compilation.
#define STRINGIZER(arg) #arg
#define STR_VALUE(arg) STRINGIZER(arg)
#define DATE_as_int_str useD(__DATE__) // What can be done ?
#define TIME_as_int_str useT(__TIME__) // What can be done ?
#define VERSION 1.4
#define COMPLETE_VERSION STR_VALUE(VERSION) "." DATE_as_int_str "." TIME_as_int_str
et obtenez COMPLETE_VERSION
sous forme de chaîne dans un const unsigned char []
.
const unsigned char completeVersion[] = ?? COMPLETE_VERSION;
Devrait sortir 1.4.1432.2234 quelque chose.
Une des solutions possibles pourrait être mais ne fonctionnait pas: convert -date -to-unsigned-int
Dans le contexte de la compilation convertint-date-et-heure-chaîne-en-entiers-en-c } _ On peut se référer à expansion-et-stringification-comment-obtenir -le-marco-nom-pas-sa-valeur
Si vous pouvez utiliser un compilateur C++ pour construire le fichier objet que vous souhaitez contenir, votre chaîne de version, nous pouvons faire exactement ce que vous voulez! La seule magie ici est que C++ vous permet d'utiliser des expressions pour initialiser un tableau de manière statique, contrairement à C. Les expressions doivent être entièrement calculables au moment de la compilation, mais ce sont des expressions qui ne posent donc aucun problème.
Nous construisons la chaîne de version, un octet à la fois, et obtenons exactement ce que nous voulons.
// source file version_num.h
#ifndef VERSION_NUM_H
#define VERSION_NUM_H
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
#endif // VERSION_NUM_H
// source file build_defs.h
#ifndef BUILD_DEFS_H
#define BUILD_DEFS_H
// Example of __DATE__ string: "Jul 27 2012"
// 01234567890
#define BUILD_YEAR_CH0 (__DATE__[ 7])
#define BUILD_YEAR_CH1 (__DATE__[ 8])
#define BUILD_YEAR_CH2 (__DATE__[ 9])
#define BUILD_YEAR_CH3 (__DATE__[10])
#define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
#define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F')
#define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
#define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p')
#define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
#define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
#define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
#define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u')
#define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S')
#define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O')
#define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N')
#define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D')
#define BUILD_MONTH_CH0 \
((BUILD_MONTH_IS_OCT || BUILD_MONTH_IS_NOV || BUILD_MONTH_IS_DEC) ? '1' : '0')
#define BUILD_MONTH_CH1 \
( \
(BUILD_MONTH_IS_JAN) ? '1' : \
(BUILD_MONTH_IS_FEB) ? '2' : \
(BUILD_MONTH_IS_MAR) ? '3' : \
(BUILD_MONTH_IS_APR) ? '4' : \
(BUILD_MONTH_IS_MAY) ? '5' : \
(BUILD_MONTH_IS_JUN) ? '6' : \
(BUILD_MONTH_IS_JUL) ? '7' : \
(BUILD_MONTH_IS_AUG) ? '8' : \
(BUILD_MONTH_IS_SEP) ? '9' : \
(BUILD_MONTH_IS_OCT) ? '0' : \
(BUILD_MONTH_IS_NOV) ? '1' : \
(BUILD_MONTH_IS_DEC) ? '2' : \
/* error default */ '?' \
)
#define BUILD_DAY_CH0 ((__DATE__[4] >= '0') ? (__DATE__[4]) : '0')
#define BUILD_DAY_CH1 (__DATE__[ 5])
// Example of __TIME__ string: "21:06:19"
// 01234567
#define BUILD_HOUR_CH0 (__TIME__[0])
#define BUILD_HOUR_CH1 (__TIME__[1])
#define BUILD_MIN_CH0 (__TIME__[3])
#define BUILD_MIN_CH1 (__TIME__[4])
#define BUILD_SEC_CH0 (__TIME__[6])
#define BUILD_SEC_CH1 (__TIME__[7])
#if VERSION_MAJOR > 100
#define VERSION_MAJOR_INIT \
((VERSION_MAJOR / 100) + '0'), \
(((VERSION_MAJOR % 100) / 10) + '0'), \
((VERSION_MAJOR % 10) + '0')
#Elif VERSION_MAJOR > 10
#define VERSION_MAJOR_INIT \
((VERSION_MAJOR / 10) + '0'), \
((VERSION_MAJOR % 10) + '0')
#else
#define VERSION_MAJOR_INIT \
(VERSION_MAJOR + '0')
#endif
#if VERSION_MINOR > 100
#define VERSION_MINOR_INIT \
((VERSION_MINOR / 100) + '0'), \
(((VERSION_MINOR % 100) / 10) + '0'), \
((VERSION_MINOR % 10) + '0')
#Elif VERSION_MINOR > 10
#define VERSION_MINOR_INIT \
((VERSION_MINOR / 10) + '0'), \
((VERSION_MINOR % 10) + '0')
#else
#define VERSION_MINOR_INIT \
(VERSION_MINOR + '0')
#endif
#endif // BUILD_DEFS_H
// source file main.c
#include "version_num.h"
#include "build_defs.h"
// want something like: 1.4.1432.2234
const unsigned char completeVersion[] =
{
VERSION_MAJOR_INIT,
'.',
VERSION_MINOR_INIT,
'-', 'V', '-',
BUILD_YEAR_CH0, BUILD_YEAR_CH1, BUILD_YEAR_CH2, BUILD_YEAR_CH3,
'-',
BUILD_MONTH_CH0, BUILD_MONTH_CH1,
'-',
BUILD_DAY_CH0, BUILD_DAY_CH1,
'T',
BUILD_HOUR_CH0, BUILD_HOUR_CH1,
':',
BUILD_MIN_CH0, BUILD_MIN_CH1,
':',
BUILD_SEC_CH0, BUILD_SEC_CH1,
'\0'
};
#include <stdio.h>
int main(int argc, char **argv)
{
printf("%s\n", completeVersion);
// prints something similar to: 1.4-V-2013-05-09T15:34:49
}
Ce n'est pas exactement le format que vous avez demandé, mais je ne comprends toujours pas comment vous voulez que les jours et les heures soient mappés sur un entier. Je pense qu'il est assez clair comment faire pour que cela produise la chaîne souhaitée.
Voici une version de travail du "build defs". Ceci est similaire à ma réponse précédente, mais j'ai calculé le mois de construction. (Vous ne pouvez pas calculer le mois de construction dans une instruction #if
, mais vous pouvez utiliser une expression ternaire qui sera compilée en constante.)
En outre, selon la documentation, si le compilateur ne peut pas obtenir l'heure, il vous donnera des points d'interrogation pour ces chaînes. J'ai donc ajouté des tests pour ce cas et obligé les différentes macros à renvoyer une valeur manifestement incorrecte (99) si cela se produit.
#ifndef BUILD_DEFS_H
#define BUILD_DEFS_H
// Example of __DATE__ string: "Jul 27 2012"
// Example of __TIME__ string: "21:06:19"
#define COMPUTE_BUILD_YEAR \
( \
(__DATE__[ 7] - '0') * 1000 + \
(__DATE__[ 8] - '0') * 100 + \
(__DATE__[ 9] - '0') * 10 + \
(__DATE__[10] - '0') \
)
#define COMPUTE_BUILD_DAY \
( \
((__DATE__[4] >= '0') ? (__DATE__[4] - '0') * 10 : 0) + \
(__DATE__[5] - '0') \
)
#define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
#define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F')
#define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
#define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p')
#define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
#define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
#define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
#define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u')
#define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S')
#define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O')
#define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N')
#define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D')
#define COMPUTE_BUILD_MONTH \
( \
(BUILD_MONTH_IS_JAN) ? 1 : \
(BUILD_MONTH_IS_FEB) ? 2 : \
(BUILD_MONTH_IS_MAR) ? 3 : \
(BUILD_MONTH_IS_APR) ? 4 : \
(BUILD_MONTH_IS_MAY) ? 5 : \
(BUILD_MONTH_IS_JUN) ? 6 : \
(BUILD_MONTH_IS_JUL) ? 7 : \
(BUILD_MONTH_IS_AUG) ? 8 : \
(BUILD_MONTH_IS_SEP) ? 9 : \
(BUILD_MONTH_IS_OCT) ? 10 : \
(BUILD_MONTH_IS_NOV) ? 11 : \
(BUILD_MONTH_IS_DEC) ? 12 : \
/* error default */ 99 \
)
#define COMPUTE_BUILD_HOUR ((__TIME__[0] - '0') * 10 + __TIME__[1] - '0')
#define COMPUTE_BUILD_MIN ((__TIME__[3] - '0') * 10 + __TIME__[4] - '0')
#define COMPUTE_BUILD_SEC ((__TIME__[6] - '0') * 10 + __TIME__[7] - '0')
#define BUILD_DATE_IS_BAD (__DATE__[0] == '?')
#define BUILD_YEAR ((BUILD_DATE_IS_BAD) ? 99 : COMPUTE_BUILD_YEAR)
#define BUILD_MONTH ((BUILD_DATE_IS_BAD) ? 99 : COMPUTE_BUILD_MONTH)
#define BUILD_DAY ((BUILD_DATE_IS_BAD) ? 99 : COMPUTE_BUILD_DAY)
#define BUILD_TIME_IS_BAD (__TIME__[0] == '?')
#define BUILD_HOUR ((BUILD_TIME_IS_BAD) ? 99 : COMPUTE_BUILD_HOUR)
#define BUILD_MIN ((BUILD_TIME_IS_BAD) ? 99 : COMPUTE_BUILD_MIN)
#define BUILD_SEC ((BUILD_TIME_IS_BAD) ? 99 : COMPUTE_BUILD_SEC)
#endif // BUILD_DEFS_H
Avec le code de test suivant, ce qui précède fonctionne très bien:
printf("%04d-%02d-%02dT%02d:%02d:%02d\n", BUILD_YEAR, BUILD_MONTH, BUILD_DAY, BUILD_HOUR, BUILD_MIN, BUILD_SEC);
Cependant, lorsque j'essaie d'utiliser ces macros avec votre macro stringing, il chaîne l'expression littérale! Je ne connais aucun moyen de faire en sorte que le compilateur réduise l'expression en un entier littéral, puis en stringize.
En outre, si vous essayez d'initialiser de manière statique un tableau de valeurs à l'aide de ces macros, le compilateur se plaint d'un message error: initializer element is not constant
. Vous ne pouvez donc pas faire ce que vous voulez avec ces macros.
À ce stade, je pense que votre meilleur choix est le script Python qui génère simplement un nouveau fichier d'inclusion pour vous. Vous pouvez pré-calculer tout ce que vous voulez dans n'importe quel format. Si vous ne voulez pas Python, nous pouvons écrire un script AWK ou même un programme C.
J'ai une réponse partielle pour vous. Ceci est basé sur ce que je reçois de GCC:
__DATE__
donne quelque chose comme "Jul 27 2012"
__TIME__
donne quelque chose comme 21:06:19
Placez ce texte dans un fichier d'inclusion appelé build_defs.h
:
#ifndef BUILD_DEFS_H
#define BUILD_DEFS_H
#define BUILD_YEAR ((__DATE__[7] - '0') * 1000 + (__DATE__[8] - '0') * 100 + (__DATE__[9] - '0') * 10 + __DATE__[10] - '0')
#define BUILD_DATE ((__DATE__[4] - '0') * 10 + __DATE__[5] - '0')
#if 0
#if (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
#define BUILD_MONTH 1
#Elif (__DATE__[0] == 'F' && __DATE__[1] == 'e' && __DATE__[2] == 'b')
#define BUILD_MONTH 2
#Elif (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
#define BUILD_MONTH 3
#Elif (__DATE__[0] == 'A' && __DATE__[1] == 'p' && __DATE__[2] == 'r')
#define BUILD_MONTH 4
#Elif (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
#define BUILD_MONTH 5
#Elif (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
#define BUILD_MONTH 6
#Elif (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
#define BUILD_MONTH 7
#Elif (__DATE__[0] == 'A' && __DATE__[1] == 'u' && __DATE__[2] == 'g')
#define BUILD_MONTH 8
#Elif (__DATE__[0] == 'S' && __DATE__[1] == 'e' && __DATE__[2] == 'p')
#define BUILD_MONTH 9
#Elif (__DATE__[0] == 'O' && __DATE__[1] == 'c' && __DATE__[2] == 't')
#define BUILD_MONTH 10
#Elif (__DATE__[0] == 'N' && __DATE__[1] == 'o' && __DATE__[2] == 'v')
#define BUILD_MONTH 11
#Elif (__DATE__[0] == 'D' && __DATE__[1] == 'e' && __DATE__[2] == 'c')
#define BUILD_MONTH 12
#else
#error "Could not figure out month"
#endif
#endif
#define BUILD_HOUR ((__TIME__[0] - '0') * 10 + __TIME__[1] - '0')
#define BUILD_MIN ((__TIME__[3] - '0') * 10 + __TIME__[4] - '0')
#define BUILD_SEC ((__TIME__[6] - '0') * 10 + __TIME__[7] - '0')
#endif // BUILD_DEFS_H
J'ai testé ce qui précède avec GCC sur Linux. Tout fonctionne très bien, sauf le problème qui me empêche de comprendre comment obtenir un chiffre pour le mois. Si vous cochez la section qui se trouve sous #if 0
, vous verrez ma tentative de déterminer le mois. GCC se plaint de ce message:
error: token ""Jul 27 2012"" is not valid in preprocessor expressions
Il serait trivial de convertir l’abréviation mensuelle de trois lettres en une sorte de numéro unique; il suffit de soustraire "A" de la première lettre et "a" de la deuxième et de la troisième, puis de convertir un nombre en base 26 ou quelque chose du genre. Mais je veux faire évaluer à 1 pour janvier et ainsi de suite, et je ne vois pas comment faire.
EDIT: Je viens de me rendre compte que vous avez demandé des chaînes, pas des expressions évaluées en valeurs entières.
J'ai essayé d'utiliser ces astuces pour construire une chaîne statique:
#define BUILD_MAJOR 1
#define BUILD_MINOR 4
#define VERSION STRINGIZE(BUILD_MAJOR) "." STRINGIZE(BUILD_MINOR)
char build_str[] = {
BUILD_MAJOR + '0', '.' BUILD_MINOR + '0', '.',
__DATE__[7], __DATE__[8], __DATE__[9], __DATE__[10],
'\0'
};
GCC se plaint que "l'élément initializer n'est pas constant" pour __DATE__
.
Désolé, je ne sais pas comment vous aider. Peut-être que vous pouvez essayer ce genre de choses avec votre compilateur? Ou peut-être que cela vous donnera une idée.
Bonne chance.
P.S. Si vous n'avez pas besoin que les choses soient numérotées et que vous souhaitiez simplement une chaîne de construction unique, rien de plus simple:
const char *build_str = "Version: " VERSION " " __DATE__ " " __TIME__;
Avec GCC, cela donne quelque chose comme:
Version: 1.4 Jul 27 2012 21:53:59
Vous pouvez toujours écrire un programme simple en Python ou quelque chose pour créer un fichier d'inclusion comportant des instructions #define
simples avec un numéro de construction, une heure et une date. Vous devrez alors exécuter ce programme avant de faire une construction.
Si vous aimez, je vais en écrire un et poster la source ici.
Si vous êtes chanceux, votre outil de construction (IDE ou autre) peut exécuter une commande externe. Il peut ensuite demander à l'outil externe de réécrire automatiquement le fichier d'inclusion à chaque construction.
EDIT: Voici un programme Python. Ceci écrit un fichier appelé build_num.h
et a un numéro de construction entier qui commence à 1 et s'incrémente à chaque fois que ce programme est exécuté; il écrit également les valeurs #define
pour l'année, le mois, la date, les heures, les minutes et les secondes de l'exécution du programme. Il comporte également un #define
pour les parties majeure et mineure du numéro de version, ainsi que les VERSION
et COMPLETE_VERSION
complets que vous vouliez. (Je ne savais pas trop ce que vous vouliez pour les chiffres de date et d'heure, alors je me suis contenté de concaténer les chiffres à partir de la date et de l'heure. Vous pouvez changer cela facilement.)
Chaque fois que vous l'exécutez, le fichier build_num.h
est lu et analysé pour le numéro de version. si le fichier build_num.h
n'existe pas, il commence le numéro de construction à 1. De même, il analyse les numéros de version majeurs et mineurs et, si le fichier n'existe pas, les remplace par défaut à la version 0.1.
import time
FNAME = "build_num.h"
build_num = None
version_major = None
version_minor = None
DEF_BUILD_NUM = "#define BUILD_NUM "
DEF_VERSION_MAJOR = "#define VERSION_MAJOR "
DEF_VERSION_MINOR = "#define VERSION_MINOR "
def get_int(s_marker, line):
_, _, s = line.partition(s_marker) # we want the part after the marker
return int(s)
try:
with open(FNAME) as f:
for line in f:
if DEF_BUILD_NUM in line:
build_num = get_int(DEF_BUILD_NUM, line)
build_num += 1
Elif DEF_VERSION_MAJOR in line:
version_major = get_int(DEF_VERSION_MAJOR, line)
Elif DEF_VERSION_MINOR in line:
version_minor = get_int(DEF_VERSION_MINOR, line)
except IOError:
build_num = 1
version_major = 0
version_minor = 1
assert None not in (build_num, version_major, version_minor)
with open(FNAME, 'w') as f:
f.write("#ifndef BUILD_NUM_H\n")
f.write("#define BUILD_NUM_H\n")
f.write("\n")
f.write(DEF_BUILD_NUM + "%d\n" % build_num)
f.write("\n")
t = time.localtime()
f.write("#define BUILD_YEAR %d\n" % t.tm_year)
f.write("#define BUILD_MONTH %d\n" % t.tm_mon)
f.write("#define BUILD_DATE %d\n" % t.tm_mday)
f.write("#define BUILD_HOUR %d\n" % t.tm_hour)
f.write("#define BUILD_MIN %d\n" % t.tm_min)
f.write("#define BUILD_SEC %d\n" % t.tm_sec)
f.write("\n")
f.write("#define VERSION_MAJOR %d\n" % version_major)
f.write("#define VERSION_MINOR %d\n" % version_minor)
f.write("\n")
f.write("#define VERSION \"%d.%d\"\n" % (version_major, version_minor))
s = "%d.%d.%04d%02d%02d.%02d%02d%02d" % (version_major, version_minor,
t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)
f.write("#define COMPLETE_VERSION \"%s\"\n" % s)
f.write("\n")
f.write("#endif // BUILD_NUM_H\n")
J'ai fait en sorte que toutes les définitions ne soient que des entiers, mais comme il s'agit de simples entiers, vous pouvez utiliser les astuces de stringing standard pour en faire une chaîne si vous le souhaitez. Vous pouvez également l'étendre de manière triviale pour créer des chaînes prédéfinies supplémentaires.
Ce programme devrait fonctionner correctement sous Python 2.6 ou version ultérieure, y compris toutes les versions de Python 3.x. Vous pouvez l'exécuter sous un ancien Python avec quelques modifications, comme ne pas utiliser .partition()
pour analyser la chaîne.
Réponse courte (version demandée): (format 3.33.20150710.182906)
S'il vous plaît, utilisez simplement un makefile
avec:
MAJOR = 3
MINOR = 33
BUILD = $(Shell date +"%Y%m%d.%H%M%S")
VERSION = "\"$(MAJOR).$(MINOR).$(BUILD)\""
CPPFLAGS = -DVERSION=$(VERSION)
program.x : source.c
gcc $(CPPFLAGS) source.c -o program.x
et si vous ne voulez pas de makefile
, encore plus court, compilez simplement avec:
gcc source.c -o program.x -DVERSION=\"2.22.$(date +"%Y%m%d.%H%M%S")\"
Réponse courte (version suggérée): (format 150710.182906)
Utilisez un double
pour le numéro de version:
MakeFile:
VERSION = $(Shell date +"%g%m%d.%H%M%S")
CPPFLAGS = -DVERSION=$(VERSION)
program.x : source.c
gcc $(CPPFLAGS) source.c -o program.x
Ou une simple commande bash:
$ gcc source.c -o program.x -DVERSION=$(date +"%g%m%d.%H%M%S")
Astuce: Vous n'aimez toujours pas makefile
ou est-ce juste pour un programme de test pas si petit? Ajouter cette ligne:
export CPPFLAGS='-DVERSION='$(date +"%g%m%d.%H%M%S")
à votre ~/.profile
, et souvenez-vous de la compilation avec gcc $CPPFLAGS ...
Longue réponse:
Je sais que cette question est plus ancienne, mais j’ai une petite contribution à apporter. La meilleure pratique consiste toujours à automatiser ce qui peut devenir une source d'erreur (ou d'oubli).
J'étais habitué à une fonction qui créait le numéro de version pour moi. Mais je préfère que cette fonction retourne un float
. Mon numéro de version peut être imprimé avec: printf("%13.6f\n", version());
, qui génère quelque chose comme: 150710.150411
(étant Année (2 chiffres) mois jour DOT heure minute secondes).
Mais bon, la question est à vous. Si vous préférez "major.minor.date.time", il faudra que ce soit une chaîne. (Croyez-moi, double c'est mieux. Si vous insistez dans une majeure, vous pouvez toujours utiliser double si vous définissez la majeure et laissez les décimales être la date et l'heure, comme: major.datetime = 1.150710150411
Permet de faire des affaires. L'exemple ci-dessous fonctionnera si vous compilez comme d'habitude, en oubliant de le définir, ou utilisez -DVERSION
pour définir la version directement à partir de Shell, mais mieux encore, je recommande la troisième option: utilisez un makefile
.
Trois formes de compilation et les résultats:
Utilisation de make:
beco> make program.x
gcc -Wall -Wextra -g -O0 -ansi -pedantic-errors -c -DVERSION="\"3.33.20150710.045829\"" program.c -o program.o
gcc program.o -o program.x
Fonctionnement:
__DATE__: 'Jul 10 2015'
__TIME__: '04:58:29'
VERSION: '3.33.20150710.045829'
Utilisation de -VERSION:
beco> gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors -DVERSION=\"2.22.$(date +"%Y%m%d.%H%M%S")\"
Fonctionnement:
__DATE__: 'Jul 10 2015'
__TIME__: '04:58:37'
VERSION: '2.22.20150710.045837'
Utilisation de la fonction intégrée:
beco> gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors
Fonctionnement:
__DATE__: 'Jul 10 2015'
__TIME__: '04:58:43'
VERSION(): '1.11.20150710.045843'
Code source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #define FUNC_VERSION (0)
6 #ifndef VERSION
7 #define MAJOR 1
8 #define MINOR 11
9 #define VERSION version()
10 #undef FUNC_VERSION
11 #define FUNC_VERSION (1)
12 char sversion[]="9999.9999.20150710.045535";
13 #endif
14
15 #if(FUNC_VERSION)
16 char *version(void);
17 #endif
18
19 int main(void)
20 {
21
22 printf("__DATE__: '%s'\n", __DATE__);
23 printf("__TIME__: '%s'\n", __TIME__);
24
25 printf("VERSION%s: '%s'\n", (FUNC_VERSION?"()":""), VERSION);
26 return 0;
27 }
28
29 /* String format: */
30 /* __DATE__="Oct 8 2013" */
31 /* __TIME__="00:13:39" */
32
33 /* Version Function: returns the version string */
34 #if(FUNC_VERSION)
35 char *version(void)
36 {
37 const char data[]=__DATE__;
38 const char tempo[]=__TIME__;
39 const char nomes[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
40 char omes[4];
41 int ano, mes, dia, hora, min, seg;
42
43 if(strcmp(sversion,"9999.9999.20150710.045535"))
44 return sversion;
45
46 if(strlen(data)!=11||strlen(tempo)!=8)
47 return NULL;
48
49 sscanf(data, "%s %d %d", omes, &dia, &ano);
50 sscanf(tempo, "%d:%d:%d", &hora, &min, &seg);
51 mes=(strstr(nomes, omes)-nomes)/3+1;
52 sprintf(sversion,"%d.%d.%04d%02d%02d.%02d%02d%02d", MAJOR, MINOR, ano, mes, dia, hora, min, seg);
53
54 return sversion;
55 }
56 #endif
Veuillez noter que la chaîne est limitée par MAJOR<=9999
et MINOR<=9999
. Bien sûr, je fixe cette valeur élevée qui, espérons-le, ne dépassera jamais. Mais utiliser double
est toujours préférable (en plus, c’est complètement automatique, il n’est pas nécessaire de régler MAJOR
et MINOR
à la main).
Maintenant, le programme ci-dessus est un peu trop. Il est préférable de supprimer complètement la fonction et de garantir que la macro VERSION
est définie, soit directement par -DVERSION
dans la ligne de commande GCC (ou un alias qui l'ajoute automatiquement pour ne pas l'oublier), ou la solution recommandée pour inclure ce processus. dans un makefile
.
Voici le makefile
que j'utilise:
Source MakeFile:
1 MAJOR = 3
2 MINOR = 33
3 BUILD = $(Shell date +"%Y%m%d.%H%M%S")
4 VERSION = "\"$(MAJOR).$(MINOR).$(BUILD)\""
5 CC = gcc
6 CFLAGS = -Wall -Wextra -g -O0 -ansi -pedantic-errors
7 CPPFLAGS = -DVERSION=$(VERSION)
8 LDLIBS =
9
10 %.x : %.c
11 $(CC) $(CFLAGS) $(CPPFLAGS) $(LDLIBS) $^ -o $@
Une meilleure version avec DOUBLE
Maintenant que je vous ai présenté "votre" solution préférée, voici ma solution:
Compilez avec (a) makefile ou (b) gcc directement:
(a) MakeFile:
VERSION = $(Shell date +"%g%m%d.%H%M%S")
CC = gcc
CFLAGS = -Wall -Wextra -g -O0 -ansi -pedantic-errors
CPPFLAGS = -DVERSION=$(VERSION)
LDLIBS =
%.x : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDLIBS) $^ -o $@
(b) Ou une simple commande bash:
$ gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors -DVERSION=$(date +"%g%m%d.%H%M%S")
Code source (version double):
#ifndef VERSION
#define VERSION version()
#endif
double version(void);
int main(void)
{
printf("VERSION%s: '%13.6f'\n", (FUNC_VERSION?"()":""), VERSION);
return 0;
}
double version(void)
{
const char data[]=__DATE__;
const char tempo[]=__TIME__;
const char nomes[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
char omes[4];
int ano, mes, dia, hora, min, seg;
char sversion[]="130910.001339";
double fv;
if(strlen(data)!=11||strlen(tempo)!=8)
return -1.0;
sscanf(data, "%s %d %d", omes, &dia, &ano);
sscanf(tempo, "%d:%d:%d", &hora, &min, &seg);
mes=(strstr(nomes, omes)-nomes)/3+1;
sprintf(sversion,"%04d%02d%02d.%02d%02d%02d", ano, mes, dia, hora, min, seg);
fv=atof(sversion);
return fv;
}
Remarque: cette double fonction n’est présente que dans le cas où vous oubliez de définir la macro VERSION. Si vous utilisez un makefile
ou définissez un alias gcc gcc -DVERSION=$(date +"%g%m%d.%H%M%S")
, vous pouvez supprimer cette fonction en toute sécurité.
Eh bien c'est ça. Un moyen simple et astucieux de configurer votre contrôle de version et de ne plus vous en soucier!
c'est très simple....
[dans make file]
==== 1 ====================
OBJS = .... \
version.o <<== add to your obj lists
==== 2 ====================
DATE = $ (date du shell + 'char szVersionStr [20] = "% Y-% m-% d% H:% M:% S";') << == add
all: version $ (ProgramID) << == version ajouter au début
version: << == add
echo '$(DATE)' > version.c <== add ( create version.c file)
[en programme]
===== 3 ==============
caractère externe szVersionStr [20];
[ en utilisant ]
=== 4 ====
printf( "Version: %s\n", szVersionStr );