ti-enxame.com

Como usar as macros predefinidas __DATE__ e __TIME__ em dois inteiros, então stringify?

Deseja usar _ DATE __ E __ TIME _ como inteiro para fornecer uma versão automatizada ao meu código em tempo de compilação.

#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

e obtenha COMPLETE_VERSION como string em um const unsigned char [].

const unsigned char completeVersion[] = ?? COMPLETE_VERSION;

Deve saída 1.4.1432.2234 algo.

Uma das possíveis soluções poderia ser, mas não funcionou: convert -date -to-unsigned-int

No contexto do tempo de compilação convertint-date-and-time-string-para-just-inteiros-em-c Pode-se referir expansão-e-string-como-obter- o-marco-name-not-its-value

34
Rick2047

Se você pode usar um compilador C++ para construir o arquivo de objeto que você quer que contenha sua string de versão, então podemos fazer exatamente o que você quer! A única mágica aqui é que o C++ permite que você use expressões para inicializar estaticamente uma matriz, enquanto C não. As expressões precisam ser totalmente computáveis ​​em tempo de compilação, mas essas expressões são, portanto, não é problema.

Construímos a string de versão um byte de cada vez e obtemos exatamente o 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 não é exatamente o formato que você pediu, mas ainda não entendo como deseja que dias e horas sejam mapeados para um número inteiro. Eu acho que é bem claro como fazer isso produzir qualquer string desejada.

40
steveha

Aqui está uma versão funcional do "build defs". Isso é semelhante à minha resposta anterior, mas descobri o mês de criação. (Você não pode calcular o mês de compilação em uma instrução #if, mas pode usar uma expressão ternária que será compilada em uma constante.)

Além disso, de acordo com a documentação, se o compilador não puder obter a hora do dia, ele fornecerá pontos de interrogação para essas cadeias de caracteres. Então eu adicionei testes para este caso e fiz as várias macros retornarem um valor obviamente errado (99) se isso acontecer.

#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

Com o seguinte código de teste, o acima funciona muito bem:

printf("%04d-%02d-%02dT%02d:%02d:%02d\n", BUILD_YEAR, BUILD_MONTH, BUILD_DAY, BUILD_HOUR, BUILD_MIN, BUILD_SEC);

No entanto, quando tento usar essas macros com sua macro de stringing, ela encadeia a expressão literal! Eu não sei de qualquer maneira de obter o compilador para reduzir a expressão para um valor inteiro literal e, em seguida, stringize.

Além disso, se você tentar inicializar estaticamente uma matriz de valores usando essas macros, o compilador reclamará com uma mensagem error: initializer element is not constant. Então você não pode fazer o que quiser com essas macros.

Neste ponto, estou pensando que sua melhor aposta é o script Python, que apenas gera um novo arquivo de inclusão para você. Você pode pré-calcular o que quiser em qualquer formato desejado. Se você não quiser o Python, podemos escrever um script AWK ou até mesmo um programa em C.

14
steveha

Eu tenho uma resposta parcial para você. Isso é baseado no que recebo do GCC:

__DATE__ dá algo como "Jul 27 2012"

__TIME__ dá algo como 21:06:19

Coloque este texto em um arquivo de inclusão chamado 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

Eu testei o acima com o GCC no Linux. Tudo funciona muito bem, exceto pelo problema que não consigo descobrir como conseguir um número para o mês. Se você verificar a seção que está em #if 0, verá minha tentativa de descobrir o mês. O GCC reclama com esta mensagem:

error: token ""Jul 27 2012"" is not valid in preprocessor expressions

Seria trivial converter a abreviação de três letras em algum tipo de número único; basta subtrair 'A' da primeira letra e 'a' da segunda e terceira, e então converter para um número de base 26 ou algo assim. Mas quero fazer com que seja avaliado em 1 para janeiro e assim por diante, e não consigo descobrir como fazer isso.

EDIT: Acabei de perceber que você pediu seqüências de caracteres, não expressões que avaliam valores inteiros.

Eu tentei usar esses truques para construir uma string 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'
};

O GCC reclama que "elemento inicializador não é constante" para __DATE__.

Desculpe, não sei como te ajudar. Talvez você possa experimentar essas coisas com o seu compilador? Ou talvez te dê uma ideia.

Boa sorte.

P.S. Se você não precisa que as coisas sejam números, e você quer apenas uma string de construção única, é fácil:

const char *build_str = "Version: " VERSION " " __DATE__ " " __TIME__;

Com o GCC, isso resulta em algo como:

Version: 1.4 Jul 27 2012 21:53:59
10
steveha

Você sempre pode escrever um programa simples em Python ou algo assim para criar um arquivo de inclusão que tenha instruções #define simples com um número de compilação, hora e data. Você precisaria executar este programa antes de fazer uma compilação.

Se você quiser, vou escrever uma e postar a fonte aqui.

Se você tiver sorte, sua ferramenta de compilação (IDE ou qualquer outra) pode ter a capacidade de executar um comando externo e, em seguida, você pode fazer com que a ferramenta externa reescreva o arquivo de inclusão automaticamente em cada compilação.

EDIT: Aqui está um programa em Python. Isso grava um arquivo chamado build_num.h e tem um número de compilação inteiro que começa em 1 e é incrementado toda vez que esse programa é executado; ele também grava os valores #define para o ano, mês, data, horas, minutos e segundos do tempo em que este programa é executado. Ele também tem um #define para partes maiores e menores do número da versão, mais as VERSION e COMPLETE_VERSION completas que você queria. (Eu não tinha certeza do que você queria para os números de data e hora, então eu escolhi apenas dígitos concatenados a partir da data e hora. Você pode mudar isso facilmente.)

Cada vez que você o executa, ele lê o arquivo build_num.h e o analisa para o número da compilação; se o arquivo build_num.h não existir, ele inicia o número de compilação em 1. Da mesma forma, ele analisa números de versão principais e secundários e, se o arquivo não existir, padroniza esses para a versão 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")

Eu fiz todos os define apenas ser inteiros, mas desde que eles são inteiros simples, você pode usar os truques de stringing padrão para construir uma string fora deles, se quiser. Também você pode trivialmente estendê-lo para construir strings pré-definidas adicionais.

Este programa deve funcionar bem sob o Python 2.6 ou posterior, incluindo qualquer versão do Python 3.x. Você poderia executá-lo sob um antigo Python com algumas alterações, como não usar .partition() para analisar a string.

6
steveha

Resposta curta (versão pedida): (formato 3.33.20150710.182906)

Por favor, use um simples makefile com:

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

e se você não quiser um makefile, menor ainda, apenas compile com:

gcc source.c -o program.x -DVERSION=\"2.22.$(date +"%Y%m%d.%H%M%S")\"

Resposta curta (versão sugerida): (formato 150710.182906)

Use um double para o número da versão:

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 um simples comando bash:

$ gcc source.c -o program.x -DVERSION=$(date +"%g%m%d.%H%M%S")

Dica: Ainda não gosto de makefile ou é apenas para um programa de teste não tão pequeno? Adicione esta linha:

 export CPPFLAGS='-DVERSION='$(date +"%g%m%d.%H%M%S")

para o seu ~/.profile, e lembre-se compilar com gcc $CPPFLAGS ...


Resposta longa:

Eu sei que esta questão é mais antiga, mas eu tenho uma pequena contribuição para fazer. A melhor prática é sempre automatizar o que de outra forma pode se tornar uma fonte de erro (ou esquecimento).

Eu estava acostumado a uma função que criou o número da versão para mim. Mas eu prefiro essa função para retornar um float. Meu número de versão pode ser impresso por: printf("%13.6f\n", version()); que emite algo como: 150710.150411 (sendo Ano (2 dígitos) mês dia DOT hora minuto segundos).

Mas, bem, a questão é sua. Se você preferir "major.minor.date.time", terá que ser uma string. (Confie em mim, o dobro é melhor. Se você insistir em um major, você ainda pode usar o dobro se definir o major e deixar os decimais serem data + hora, como: major.datetime = 1.150710150411

Vamos ao negócio. O exemplo abaixo funcionará se você compilar como de costume, esquecendo de configurá-lo, ou usar -DVERSION para definir a versão diretamente do Shell, mas o melhor de tudo, eu recomendo a terceira opção: use um makefile.


Três formas de compilação e os 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

Corrida:

__DATE__: 'Jul 10 2015'
__TIME__: '04:58:29'
VERSION: '3.33.20150710.045829'

Usando -DVERSÃO:

beco> gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors -DVERSION=\"2.22.$(date +"%Y%m%d.%H%M%S")\"

Corrida:

__DATE__: 'Jul 10 2015'
__TIME__: '04:58:37'
VERSION: '2.22.20150710.045837'

Usando a função embutida:

beco> gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors

Corrida:

__DATE__: 'Jul 10 2015'
__TIME__: '04:58:43'
VERSION(): '1.11.20150710.045843'

Código fonte

  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

Por favor, note que a string é limitada por MAJOR<=9999 e MINOR<=9999. Claro, eu configurei esse valor alto que provavelmente nunca transbordaria. Mas usar double ainda é melhor (mais, é completamente automático, não é necessário definir MAJOR e MINOR manualmente).

Agora, o programa acima é um pouco demais. Melhor é remover completamente a função e garantir que a macro VERSION seja definida, seja por -DVERSION diretamente na linha de comando do GCC (ou um alias que a adicione automaticamente, assim você não pode esquecer), ou a solução recomendada, para incluir este processo em um makefile.

Aqui está o makefile eu uso:


fonte 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 [email protected]

Uma versão melhor com DOUBLE

Agora que eu apresentei "sua" solução preferida, aqui está minha solução:

Compilar com (a) makefile ou (b) gcc diretamente:

(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 [email protected]

(b) Ou um simples 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 fonte (versão dupla):

#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 função dupla existe apenas no caso de você se esquecer de definir a macro VERSÃO. Se você usar um makefile ou definir um alias gcc gcc -DVERSION=$(date +"%g%m%d.%H%M%S"), poderá excluir essa função completamente.


Bem, é isso. Uma maneira muito simples e fácil de configurar o controle de versão e nunca mais se preocupar com isso!

3
Dr Beco

É muito simples....

[no arquivo make]

==== 1 ===================

OBJS = .... \

version.o <<== add to your obj lists

==== 2 ===================

DATE = $ (data do shell + 'char szVersionStr [20] = "% Y-% m-% d% H:% M:% S";') << == adicionar 

all: version $ (ProgramID) << == versão adiciona primeiro

versão: << == adicionar

echo '$(DATE)' > version.c  <== add ( create version.c file)

[no programa]

===== 3 =============

extern char szVersionStr [20];

[usando]

=== 4 ====

printf( "Version: %s\n", szVersionStr );
0
turningeagle