Как использовать предопределенные макросы __DATE__ и __TIME__ как два целых числа, а затем stringify?
Хотите использовать __ 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-date-to-unsigned-int
В контексте времени компиляции /questions/2188074/preobrazovat-stroku-datyi-i-vremeni-v-tselyie-chisla-v-c Можно ссылаться на расширение-и-строку-как-то-получить-марко-имя-не-его-значение
6 ответов
Если вы можете использовать компилятор 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
}
Это не совсем тот формат, который вы просили, но я до сих пор не до конца понимаю, как вы хотите, чтобы дни и часы отображались в целое число. Я думаю, что довольно ясно, как заставить это произвести любую желаемую последовательность.
Вот рабочая версия "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 или даже программу на Си.
У меня есть частичный ответ для вас. Это основано на том, что я получаю от 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__
,
Извините, я не уверен, как вам помочь. Может быть, вы можете попробовать это с вашим компилятором? Или, может быть, это даст вам идею.
Удачи.
PS Если вам не нужны числа, чтобы быть числами, и вы просто хотите уникальную строку сборки, это легко:
const char *build_str = "Version: " VERSION " " __DATE__ " " __TIME__;
С GCC это приводит к чему-то вроде:
Version: 1.4 Jul 27 2012 21:53:59
Вы всегда можете написать простую программу на 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()
разобрать строку.
Краткий ответ (запрашиваемая версия): (формат 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", это должна быть строка. (Поверьте мне, double лучше. Если вы настаиваете на мажоре, вы все равно можете использовать double, если вы задаете мажор и разрешаете использовать десятичные дроби как дату + время, например 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 $@
Лучшая версия с двойной
Теперь, когда я представил вам "ваше" предпочтительное решение, вот мое решение:
Скомпилируйте с (a) makefile или (b) 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 $@
(б) Или простая команда 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")
Вы можете безопасно удалить эту функцию полностью.
Ну вот и все. Очень аккуратный и простой способ настроить контроль версий и больше никогда не беспокоиться об этом!
Для тех, кто просто хочет заменить лишнее ' ' (пробел), если день меньше 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] }
Результат: 6 сентября 2019 г.
Это очень просто....
[в файле make]
==== 1 ===================
OBJS =.... \
version.o <<== add to your obj lists
==== 2 ===================
DATE = $ (дата оболочки +'char szVersionStr[20] = "%Y-%m-%d %H:%M:%S";') <<== добавить
все: версия $(ProgramID) <<== версия добавить сначала
версия: << == добавить
echo '$(DATE)' > version.c <== add ( create version.c file)
[в программе]
===== 3 =============
extern char szVersionStr [20];
[ с помощью ]
=== 4 ====
printf( "Version: %s\n", szVersionStr );