¿Cómo usar las macros predefinidas__DATE__ y__TIME__ en dos enteros, y luego stringify?
predefined-macro (6)
Aquí hay una versión funcional de los "build defs". Esto es similar a mi respuesta anterior pero descubrí el mes de compilación. (No se puede calcular el mes de compilación en una instrucción #if
, pero puede usar una expresión ternaria que se compilará como una constante).
Además, según la documentación, si el compilador no puede obtener la hora del día, le dará signos de interrogación para estas cadenas. Así que agregué pruebas para este caso e hice que las distintas macros devuelvan un valor obviamente incorrecto (99) si esto sucede.
#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
Con el siguiente código de prueba, lo anterior funciona muy bien:
printf("%04d-%02d-%02dT%02d:%02d:%02d/n", BUILD_YEAR, BUILD_MONTH, BUILD_DAY, BUILD_HOUR, BUILD_MIN, BUILD_SEC);
Sin embargo, cuando trato de usar esas macros con su macro de stringing, ¡se stringize la expresión literal! No sé de ninguna manera para hacer que el compilador reduzca la expresión a un valor entero literal y luego haga una cadena.
Además, si intenta inicializar estáticamente una matriz de valores utilizando estas macros, el compilador se queja con un error: initializer element is not constant
mensaje error: initializer element is not constant
. Entonces no puedes hacer lo que quieras con estas macros.
En este punto, creo que la mejor opción es la secuencia de comandos de Python que solo genera un nuevo archivo de inclusión para usted. Puede calcular previamente cualquier cosa que desee en el formato que desee. Si no quiere Python, podemos escribir un script AWK o incluso un programa C.
Desea usar __ DATE __ y __ TIME __ como un número entero para dar una versión automatizada a mi código en tiempo de compilación.
#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
y obtener COMPLETE_VERSION
como cadena en un const unsigned char []
.
const unsigned char completeVersion[] = ?? COMPLETE_VERSION;
Debería producir 1.4.1432.2234 algo.
Una de las posibles soluciones podría ser, pero no funcionó: convert-date-to-unsigned-int
En el contexto del tiempo de compilación convertint-date-and-time-string-to-just-integers-in-c Uno puede referirse a la expanssion-and-stringification-how-to-get-the-marco-name-not-its-value
Es muy simple....
[en el archivo make]
==== 1 ===================
OBJS = .... /
version.o <<== add to your obj lists
==== 2 ===================
FECHA = $ (fecha de shell + ''char szVersionStr [20] = "% Y-% m-% d% H:% M:% S";'') << == agregar
todo: versión $ (ProgramID) << == versión agregar al principio
versión: << == agregar
echo ''$(DATE)'' > version.c <== add ( create version.c file)
[en el programa]
===== 3 =============
extern char szVersionStr [20];
[ utilizando ]
=== 4 ====
printf( "Version: %s/n", szVersionStr );
Si puede usar un compilador de C ++ para compilar el archivo de objeto que desea que contenga su cadena de versión, ¡podemos hacer exactamente lo que usted quiere! La única magia aquí es que C ++ le permite usar expresiones para inicializar estáticamente una matriz, mientras que C no lo hace. Las expresiones deben ser totalmente computables en tiempo de compilación, pero estas expresiones son, por lo que no es problema.
Construimos la cadena de versiones de un byte a la vez, y obtenemos exactamente lo que queremos.
// 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
}
Este no es exactamente el formato que solicitó, pero aún no entiendo completamente cómo desea que se asignen días y horas a un número entero. Creo que está bastante claro cómo hacer que este produzca cualquier cadena deseada.
Siempre puede escribir un programa simple en Python o algo así para crear un archivo de inclusión que tenga declaraciones simples #define
con un número de compilación, hora y fecha. Entonces necesitarías ejecutar este programa antes de hacer una compilación.
Si quieres, escribiré una y publicaré la fuente aquí.
Si tiene suerte, su herramienta de compilación (IDE o lo que sea) podría tener la capacidad de ejecutar un comando externo, y luego podría hacer que la herramienta externa reescriba automáticamente el archivo de inclusión con cada compilación.
EDITAR: Aquí hay un programa de Python. Esto escribe un archivo llamado build_num.h
y tiene un número de compilación entero que comienza en 1 e incrementa cada vez que se ejecuta este programa; también escribe valores #define
para el año, mes, fecha, horas, minutos y segundos de la hora en que se ejecuta este programa. También tiene #define
para partes mayores y menores del número de versión, más la VERSION
completa y el COMPLETE_VERSION
que usted desea. (No estaba seguro de lo que quería para los números de fecha y hora, así que solo busqué dígitos concatenados a partir de la fecha y la hora. Puede cambiar esto fácilmente).
Cada vez que lo ejecuta, lee en el archivo build_num.h
y lo analiza para el número de compilación; si el archivo build_num.h
no existe, inicia el número de compilación en 1. Del mismo modo, analiza números de versiones mayores y menores, y si el archivo no existe, los valores predeterminados son los de la versión 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")
Hice que todas las definiciones solo fueran números enteros, pero dado que son enteros simples, puede usar los trucos de stringizing estándar para construir una cadena de ellos si lo desea. También puede ampliarlo trivialmente para crear cadenas adicionales predefinidas.
Este programa debería funcionar bien en Python 2.6 o posterior, incluida cualquier versión de Python 3.x. Podrías ejecutarlo en un viejo Python con algunos cambios, como no usar .partition()
para analizar la cadena.
Tengo una respuesta parcial para ti. Esto se basa en lo que recibo de GCC:
__DATE__
da algo como "Jul 27 2012"
__TIME__
da algo así como 21:06:19
Coloque este texto en un archivo de inclusión llamado 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
Probé lo anterior con GCC en Linux. Todo funciona muy bien, excepto por el problema de que no puedo averiguar cómo obtener un número para el mes. Si #if 0
la sección que está debajo de #if 0
, verá mi intento de averiguar el mes. GCC se queja con este mensaje:
error: token ""Jul 27 2012"" is not valid in preprocessor expressions
Sería trivial convertir la abreviatura de tres letras en algún tipo de número único; simplemente resta ''A'' de la primera letra y ''a'' de la segunda y la tercera, y luego convierte a un número de base 26 o algo así. Pero quiero que evalúe a 1 para enero y así sucesivamente, y no puedo encontrar la manera de hacerlo.
EDITAR: Me acabo de dar cuenta de que pediste cadenas, no expresiones que evalúen valores enteros.
Traté de usar estos trucos para construir una cadena estática:
#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 queja de que "el elemento inicializador no es constante" para __DATE__
.
Lo siento, no estoy seguro de cómo ayudarte. ¿Tal vez puedas probar esto con tu compilador? O tal vez te dará una idea.
Buena suerte.
PD: si no necesitas que las cosas sean números, y solo quieres una cadena de compilación única, es fácil:
const char *build_str = "Version: " VERSION " " __DATE__ " " __TIME__;
Con GCC, esto da como resultado algo así como:
Version: 1.4 Jul 27 2012 21:53:59
Respuesta corta (versión solicitada): (formato 3.33.20150710.182906)
Por favor, simplemente use un makefile
con:
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
y si no quiere un makefile
, más corto aún, solo compile con:
gcc source.c -o program.x -DVERSION=/"2.22.$(date +"%Y%m%d.%H%M%S")/"
Respuesta corta (versión sugerida): (formato 150710.182906)
Use un double
para el número de versión:
MakeFile:
VERSION = $(shell date +"%g%m%d.%H%M%S")
CPPFLAGS = -DVERSION=$(VERSION)
program.x : source.c
gcc $(CPPFLAGS) source.c -o program.x
O un simple comando bash:
$ gcc source.c -o program.x -DVERSION=$(date +"%g%m%d.%H%M%S")
Consejo: ¿ Todavía no le gusta el makefile
o es solo para un programa de prueba que no es tan pequeño? Agregue esta línea:
export CPPFLAGS=''-DVERSION=''$(date +"%g%m%d.%H%M%S")
a su ~/.profile
, y recuerde compilar con gcc $CPPFLAGS ...
Respuesta larga:
Sé que esta pregunta es más antigua, pero tengo que hacer una pequeña contribución. La mejor práctica es automatizar siempre lo que de lo contrario puede convertirse en una fuente de error (o olvido).
Estaba acostumbrado a una función que creó el número de versión para mí. Pero prefiero esta función para devolver un float
. Mi número de versión puede imprimirse por: printf("%13.6f/n", version());
que emite algo así como: 150710.150411
(siendo Año (2 dígitos) mes día DOT hora minuto segundos).
Pero, bueno, la pregunta es tuya. Si prefiere "major.minor.date.time", tendrá que ser una cadena. (Confía en mí, el doble es mejor. Si insistes en un especial, aún puedes usar el doble si configuras el principal y dejas que los decimales sean fecha + hora, como: major.datetime = 1.150710150411
Vamos a hacer negocios. El siguiente ejemplo funcionará si compila como de costumbre, olvidándose de configurarlo, o usa -DVERSION
para configurar la versión directamente desde el shell, pero lo mejor de todo es que recomiendo la tercera opción: use un makefile
.
Tres formas de compilación y los resultados:
Usando 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
Corriendo:
__DATE__: ''Jul 10 2015''
__TIME__: ''04:58:29''
VERSION: ''3.33.20150710.045829''
Usando -DVERSIÓN:
beco> gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors -DVERSION=/"2.22.$(date +"%Y%m%d.%H%M%S")/"
Corriendo:
__DATE__: ''Jul 10 2015''
__TIME__: ''04:58:37''
VERSION: ''2.22.20150710.045837''
Usando la función incorporada:
beco> gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors
Corriendo:
__DATE__: ''Jul 10 2015''
__TIME__: ''04:58:43''
VERSION(): ''1.11.20150710.045843''
Código fuente
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
Tenga en cuenta que la cadena está limitada por MAJOR<=9999
y MINOR<=9999
. Por supuesto, establezco este alto valor que, afortunadamente, nunca se desbordará. Pero el uso del double
es aún mejor (además, es completamente automático, no es necesario configurar MAJOR
y MINOR
a mano).
Ahora, el programa anterior es demasiado. Mejor es eliminar completamente la función y garantizar que la macro VERSION
esté definida, ya sea por -DVERSION
directamente en la línea de comando de GCC (o un alias que se agrega automáticamente para que no se olvide), o la solución recomendada, para incluir esto procesar en un makefile
.
Aquí está el makefile
que uso:
Fuente 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 $@
Una mejor versión con DOUBLE
Ahora que le presenté "su" solución preferida, aquí está mi solución:
Compila con (a) makefile o (b) gcc directamente:
(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) O un simple comando bash:
$ gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors -DVERSION=$(date +"%g%m%d.%H%M%S")
Código fuente (versión doble):
#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;
}
Nota: esta doble función solo existe en caso de que olvide definir macro VERSIÓN. Si usa un makefile
o establece un alias gcc gcc -DVERSION=$(date +"%g%m%d.%H%M%S")
, puede eliminar esta función completamente.
Bueno, eso es todo. ¡Una forma muy fácil y sencilla de configurar su control de versión y no preocuparse nunca más por ello!