Как использовать предопределенные макросы __DATE__ и __TIME__ в виде двух целых чисел, а затем преобразовать в строку?

Хотите использовать __ DATE __ и __ TIME __ как целое число для автоматической версии моего кода во время компиляции.

#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

и получить COMPLETE_VERSION в виде строки в const unsigned char [].

const unsigned char completeVersion[] = ?? COMPLETE_VERSION;

Должен вывод 1.4.1432.2234 что-то.

Одним из возможных решений могло быть, но оно не сработало: convert -дата-to-unsigned-int

В контексте времени компиляции convertint-date-and-time -string-to-just-integers-in-c Можно обратиться к расширение-and-stringification-how-to-get-marco-name-not-its-value


person Rick2047    schedule 28.07.2012    source источник
comment
Ваш макрос _VERSION нарушает правило идентификатора, которое начинается с подчеркивания, за которым следует заглавная буква, зарезервировано для реализации.   -  person chris    schedule 28.07.2012
comment
@chris: я изменю идентификатор. поставлю что-нибудь другое. в моем коде уже есть другая переменная «ВЕРСИЯ», поэтому я думаю, что можно сделать.   -  person Rick2047    schedule 28.07.2012
comment
Если он не начинается с двух символов подчеркивания, все должно быть в порядке.   -  person chris    schedule 28.07.2012
comment
Попытка mail-archive.com/avr-gcc-list @nongnu.org/msg05718.html, но не смог заставить его работать.   -  person Rick2047    schedule 28.07.2012
comment
Я могу легко преобразовать целочисленный макрос в строку, но как это сделать, если он уже является строкой? В основном нужно обрабатывать его частями строки во время самой компиляции.   -  person Rick2047    schedule 28.07.2012
comment
У каждого инструмента автоматизации сборки есть способ автоматически сгенерировать номер версии. Вы должны упомянуть, какой из них вы используете.   -  person Hans Passant    schedule 28.07.2012
comment
Пожалуйста, пройдите по всем справочным ссылкам. У меня нет такой свободы. Я использую компилятор, который генерирует код для чипа в виде шестнадцатеричного файла. Который я должен загрузить в чип как программу. Должен ли я добавить эту информацию в этот вопрос/   -  person Rick2047    schedule 28.07.2012
comment
Кажется, мне нужно выяснить, как символы объединяются во время компиляции. __TIME__[0] дает char !!   -  person Rick2047    schedule 28.07.2012
comment
Вам действительно нужен какой-то определенный формат, как вы показали? Если вы делаете шестнадцатеричный код для встраиваемых файлов, скажите своему компоновщику выделить в памяти специальное место, достаточно большое для хранения самого большого формата. Затем просто поместите следующий const char version[] = DATE__## ##__TIME; Тогда читатель этой информации должен будет правильно ее расшифровать. Перейдите по адресу 0xXX, прочитайте Y байтов, и вуаля, у вас есть дополнительный специальный код даты. Кстати, это всего лишь биты, поэтому вы можете прочитать их в шестнадцатеричном формате, если хотите, в конце концов, это просто уникальная серия битов, которая идентифицирует сборку.   -  person Josh Petitt    schedule 28.07.2012
comment
В настоящее время я собираюсь с тем же, как вы упомянули. Но мне нужен формат, который я упомянул. Если это можно сделать, то лучше. Иначе будет то же самое. :| Другое будет хорошим знанием, чтобы иметь здесь.   -  person Rick2047    schedule 28.07.2012
comment
Ваш пример строки: 1.4.1432.2234 Не могли бы вы объяснить, почему 1432 — это дата, а 2234 — это время? Это время просто 22:34 (то есть 10:34 P.M.)?   -  person steveha    schedule 10.05.2013
comment
Вам нужно, чтобы это был чистый C, или вы можете скомпилировать его с помощью компилятора C++?   -  person steveha    schedule 10.05.2013
comment
Мне нужно это на C. Я хотел бы узнать, как это будет сделано на C++. Пожалуйста, перейдите по ссылкам ( velocityreviews.com/forums/t316565-convert-date-to-unsigned-int.html ) для преобразования даты в целое число.   -  person Rick2047    schedule 16.05.2013


Ответы (7)


Если вы можете использовать компилятор C++ для создания объектного файла, который должен содержать строку вашей версии, то мы можем сделать именно то, что вы хотите! Единственная магия здесь в том, что C++ позволяет использовать выражения для статической инициализации массива, а C — нет. Выражения должны быть полностью вычислимыми во время компиляции, но эти выражения таковы, так что это не проблема.

Мы строим строку версии по одному байту за раз и получаем именно то, что хотим.

// 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
}

Это не совсем тот формат, о котором вы просили, но я до сих пор не совсем понимаю, как вы хотите, чтобы дни и часы отображались в целое число. Я думаю, довольно ясно, как заставить это производить любую желаемую строку.

person steveha    schedule 09.05.2013
comment
Раскрывает ли процессор все эти макросы — все операторы if и тернарные операторы. Или они оцениваются во время компиляции? - person Tomáš Zato - Reinstate Monica; 25.01.2016
comment
Вычисляется во время компиляции, если выражение использует только постоянные данные, известные во время компиляции, как показано здесь. - person steveha; 30.01.2016
comment
есть версия для C? - person Hasan alattar; 12.06.2019
comment
@Hasanalattar Как объяснялось в первом абзаце ответа, препроцессор в C не позволяет использовать приемы, необходимые для этого ответа; препроцессор в C++ позволяет использовать трюки. Однако результатом вышеизложенного является то, что вы можете получить именно ту строку, которую хотите, в объектном файле, и вы можете без проблем связать ее с кодом C. Связывание кода из C++ с кодом C может быть проблематичным, но это простые строковые данные. Таким образом, у вас может быть целый проект на C, скомпилированный с помощью компилятора C, и только один строковый файл версии, скомпилированный компилятором C++, и он без проблем линкуется. - person steveha; 12.06.2019
comment
ВНИМАНИЕ, что он обновляется только при перекомпиляции содержащего его файла. Изменение какого-либо другого исходного файла и повторное связывание проекта не приведет к обновлению даты, если вы хотите этого, вам нужна система сборки, которая либо всегда выполняет полную перекомпиляцию, либо имеет специальное правило, всегда принудительно перекомпилирующее исходный файл, содержащий это. - person Chris Stratton; 14.07.2019

Вот рабочая версия "build defs". Это похоже на мой предыдущий ответ, но я выяснил месяц сборки. (Вы просто не можете вычислить месяц сборки в операторе #if, но вы можете использовать троичное выражение, которое будет скомпилировано в константу.)

Кроме того, согласно документации, если компилятор не может получить время суток, он поставит вам вопросительные знаки для этих строк. Поэтому я добавил тесты для этого случая и заставил различные макросы возвращать явно неправильное значение (99), если это произойдет.

#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

Со следующим тестовым кодом вышеизложенное отлично работает:

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

Однако, когда я пытаюсь использовать эти макросы с вашим строковым макросом, он преобразует буквальное выражение в строку! Я не знаю, как заставить компилятор сократить выражение до буквального целочисленного значения, а затем преобразовать его в строку.

Кроме того, если вы попытаетесь статически инициализировать массив значений с помощью этих макросов, компилятор выдаст сообщение error: initializer element is not constant. Таким образом, вы не можете делать то, что хотите, с этими макросами.

На данный момент я думаю, что вам лучше всего подойдет скрипт Python, который просто генерирует для вас новый включаемый файл. Вы можете предварительно вычислить все, что хотите, в любом формате. Если вам не нужен Python, мы можем написать сценарий AWK или даже программу на C.

person steveha    schedule 09.05.2013

У меня есть частичный ответ для вас. Это основано на том, что я получаю от GCC:

__DATE__ дает что-то вроде "Jul 27 2012"

__TIME__ дает что-то вроде 21:06:19

Поместите этот текст во включаемый файл с именем 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

Я протестировал это с помощью GCC в Linux. Все отлично работает, за исключением проблемы, что я не могу понять, как получить число за месяц. Если вы проверите раздел под #if 0, вы увидите мою попытку определить месяц. GCC жалуется на это сообщение:

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

Было бы тривиально преобразовать трехбуквенную аббревиатуру месяца в какое-то уникальное число; просто вычтите «А» из первой буквы и «а» из второй и третьей, а затем преобразуйте в число с основанием 26 или что-то в этом роде. Но я хочу, чтобы он оценивался как 1 для января и т. д., и я не могу понять, как это сделать.

РЕДАКТИРОВАТЬ: я только что понял, что вы запрашивали строки, а не выражения, которые оцениваются как целые значения.

Я попытался использовать эти приемы для создания статической строки:

#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 жалуется, что «элемент инициализатора не является постоянным» для __DATE__.

Извините, я не знаю, как вам помочь. Может быть, вы можете попробовать это с вашим компилятором? Или, может быть, это даст вам представление.

Удачи.

P.S. Если вам не нужны числа, и вам просто нужна уникальная строка сборки, это легко сделать:

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

С GCC это приводит к чему-то вроде:

Version: 1.4 Jul 27 2012 21:53:59
person steveha    schedule 28.07.2012
comment
Мне нужно правильно указать строку #define BUILD_HOUR (( __ TIME __ [0] - '0') * 10 + TIME [1] - '0') . Если я использую const unsigned char df [] = BUILD_HOUR . Он ничего не печатает или печатает эквивалентное значение int. Если я укажу, что это дает отпечатки ((10:24:33[0] - '0') * 10 + 10:24:33[1] - '0') . Дело в том, что __TIME __ уже является строкой. - person Rick2047; 28.07.2012
comment
Думаю, придется использовать Версия: 1.4, 27 июля 2012 г., 21:53:59 . Но все еще смотрю, получу ли я еще несколько ответов. - person Rick2047; 28.07.2012
comment
Кажется, кроме тебя, никто не хочет принимать вызов. Я возьму это. :П - person Rick2047; 28.07.2012
comment
Просто идея уменьшить количество тестов в #if. Некоторых проверок достаточно, чтобы распознать месяц, например: __DATE__[0] == 'F' однозначно февраль или __DATE__[2] == 'l' определенно июль. Это может уменьшить количество проверок и сделать его немного менее тяжелым. - person Patrick Schlüter; 28.07.2012
comment
@tristopia - я мог бы изменить его, чтобы уменьшить количество проверок, если это действительно сработало. Так как не работает, то оставил как есть. - person steveha; 28.07.2012

Вы всегда можете написать простую программу на Python или что-то еще, чтобы создать включаемый файл, содержащий простые операторы #define с номером сборки, временем и датой. Затем вам нужно будет запустить эту программу перед выполнением сборки.

Если хотите, я напишу один и опубликую здесь исходный код.

Если вам повезет, ваш инструмент сборки (IDE или что-то еще) может иметь возможность запускать внешнюю команду, и тогда вы можете заставить внешний инструмент автоматически перезаписывать включаемый файл при каждой сборке.

РЕДАКТИРОВАТЬ: Вот программа Python. Это записывает файл с именем build_num.h и имеет целочисленный номер сборки, который начинается с 1 и увеличивается при каждом запуске этой программы; он также записывает значения #define для года, месяца, даты, часов, минут и секунд времени, когда эта программа запущена. Он также имеет #define для основных и второстепенных частей номера версии, а также полные VERSION и COMPLETE_VERSION, которые вы хотели. (Я не был уверен, что вы хотели для чисел даты и времени, поэтому я выбрал просто конкатенированные цифры из даты и времени. Вы можете легко изменить это.)

Каждый раз, когда вы запускаете его, он читает файл build_num.h и анализирует его на наличие номера сборки; если файл build_num.h не существует, он начинает номер сборки с 1. Аналогичным образом он анализирует номера основных и дополнительных версий, и, если файл не существует, по умолчанию используется версия 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")

Я сделал все определения просто целыми числами, но, поскольку они являются простыми целыми числами, вы можете использовать стандартные трюки со строками, чтобы построить из них строку, если хотите. Также вы можете тривиально расширить его для создания дополнительных предопределенных строк.

Эта программа должна нормально работать под Python 2.6 или более поздней версии, включая любую версию Python 3.x. Вы можете запустить его под старым Python с несколькими изменениями, например, не используя .partition() для разбора строки.

person steveha    schedule 28.07.2012
comment
Да, у меня есть Post и Pre "Build Actions". Это хорошая идея. И приемлемо для меня. Я могу использовать любое приложение для этого. Я пошел с простой датой и временем. Единственное, что «Действие сборки» должно быть блокирующим действием. Если это так, то это лотерея. Спасибо за идею gr8. Этот ответ также стоит проголосовать. Должен ли я выбрать этот ответ как подходящий ответ. Это другой вид ответа за пределами измерений. Ожидание голосования в качестве ответа. - person Rick2047; 29.07.2012
comment
Вам решать, принимать ответ или нет. Я думаю, что у этого вопроса было мало просмотров; мало кто его даже видел, и я пока единственный, кто на него ответил. Так что вам, возможно, придется подождать некоторое время, пока сообщество не проголосует за мой ответ. - person steveha; 31.07.2012

Краткий ответ (версия с вопросом): (формат 3.33.20150710.182906)

Пожалуйста, просто используйте makefile с:

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

и если вы не хотите makefile, еще короче, просто скомпилируйте с помощью:

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

Краткий ответ (предлагаемый вариант): (формат 150710.182906)

Используйте double для номера версии:

MakeFile:

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

Или простая команда bash:

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

Совет. Все еще не нравится makefile или это просто не очень маленькая тестовая программа? Добавьте эту строку:

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

к вашему ~/.profile, и не забудьте скомпилировать с gcc $CPPFLAGS ...


Длинный ответ:

Я знаю, что этот вопрос старше, но у меня есть небольшой вклад. Лучшей практикой всегда является автоматизация того, что в противном случае может стать источником ошибок (или забвения).

Я привык к функции, которая создавала для меня номер версии. Но я предпочитаю, чтобы эта функция возвращала float. Мой номер версии может быть напечатан с помощью: printf("%13.6f\n", version());, который выдает что-то вроде: 150710.150411 (год (2 цифры) месяц день DOT час минута секунд).

Но, хорошо, вопрос к вам. Если вы предпочитаете "major.minor.date.time", это должна быть строка. (Поверьте мне, двойное число лучше. Если вы настаиваете на мажоре, вы все равно можете использовать двойное значение, если установите мажор и разрешите десятичным дробям быть датой + временем, например: major.datetime = 1.150710150411

Приступим к делу. Пример ниже будет работать, если вы компилируете как обычно, забыв его установить, или используете -DVERSION для установки версии непосредственно из оболочки, но лучше всего я рекомендую третий вариант: используйте makefile.


Три формы компиляции и результаты:

Использование 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

Бег:

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

Использование -DVERSION:

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

Бег:

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

Использование встроенной функции:

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

Бег:

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

Исходный код

  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

Обратите внимание, что строка ограничена MAJOR<=9999 и MINOR<=9999. Конечно, я установил это высокое значение, которое, надеюсь, никогда не переполнится. Но использовать double все же лучше (плюс, это полностью автоматически, не нужно вручную задавать MAJOR и MINOR).

Теперь программа выше - это слишком много. Лучше полностью удалить функцию и гарантировать, что макрос VERSION определен либо -DVERSION непосредственно в командной строке GCC (или псевдонимом, который автоматически добавляет его, чтобы вы не могли забыть), либо рекомендуемым решением, чтобы включить этот процесс в makefile.

Вот makefile, который я использую:


Источник 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 $@

Улучшенная версия с DOUBLE

Теперь, когда я представил вам «ваше» предпочтительное решение, вот мое решение:

Скомпилировать напрямую с помощью (а) makefile или (б) gcc:

(а) 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) Или простая команда bash:

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

Исходный код (двойная версия):

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

Примечание: эта двойная функция существует только в том случае, если вы забыли определить макрос VERSION. Если вы используете makefile или устанавливаете alias gcc gcc -DVERSION=$(date +"%g%m%d.%H%M%S"), вы можете полностью удалить эту функцию.


Вот и все. Очень аккуратный и простой способ настроить контроль версий и больше никогда об этом не беспокоиться!

person DrBeco    schedule 10.07.2015
comment
никогда не используйте двоичную переменную с плавающей запятой, если вам нужно точное десятичное представление. - person Javier; 11.08.2015
comment
Я вижу вашу точку зрения. Есть способы обойти, если произойдет какой-то раунд. Давно пользуюсь этим форматом и никогда не было проблем. Кроме того, его легко заметить, и он почти является инокулятом. Я бы согласился в какой-нибудь более опасной ситуации. Не здесь. В любом случае, простой обходной путь состоит в том, чтобы добавить немного больше нулей в конце, а затем 5. Version 1.1508122101150005. Этого с использованием double вместо float будет достаточно. Просто игнорируйте числа после 12-го десятичного знака. Он не изменит последний (в данном случае число секунд). В любом случае, приятное наблюдение. - person DrBeco; 13.08.2015
comment
Это единственный разумный и надежный ответ здесь. - person Jason C; 16.05.2021

Для тех, кто просто хочет заменить дополнительный ' ' (пробел), если день меньше 10, используйте:

#define BUILD_DATE (char const[]) { __DATE__[0], __DATE__[1], __DATE__[2], __DATE__[3], (__DATE__[4] == ' ' ?  '0' : __DATE__[4]), __DATE__[5], __DATE__[6], __DATE__[7], __DATE__[8], __DATE__[9], __DATE__[10], __DATE__[11] }

Вывод: 06 сентября 2019 г.

person hueman    schedule 06.09.2019

это очень просто....

[в файле сборки]

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

ОБЪЕКТЫ = ....\

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

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

DATE = $(дата оболочки +'char szVersionStr[20] = "%Y-%m-%d %H:%M:%S";') ‹‹== добавить

all:version $(ProgramID) ‹‹== версия добавляется первой

версия: ‹‹== добавить

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

[в программе]

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

внешний символ szVersionStr[20];

[ с использованием ]

=== 4 ====

printf( "Version: %s\n", szVersionStr );
person turningeagle    schedule 08.06.2016