如何使用__DATE__和__TIME__预定义的宏作为两个整数,然后stringify?

Ric*_*047 37 c macros predefined-macro

想要使用__ 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
Run Code Online (Sandbox Code Playgroud)

并获得COMPLETE_VERSION一个字符串const unsigned char [].

const unsigned char completeVersion[] = ?? COMPLETE_VERSION;
Run Code Online (Sandbox Code Playgroud)

应输出1.4.1432.2234的东西.

其中一个可能的解决方案可能是但不起作用:convert - date -to-unsigned-int

在编译时的上下文中,convertint-date-and-time-string-to-just-integers-in-c 可以参考expand-and-stringification-how-to-get-the-marco-name-not-its-value

ste*_*eha 47

如果您可以使用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
Run Code Online (Sandbox Code Playgroud)
// 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
Run Code Online (Sandbox Code Playgroud)
// 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
}
Run Code Online (Sandbox Code Playgroud)

这不是您要求的格式,但我仍然不完全了解您希望如何将天数和小时映射到整数.我认为如何使这产生任何所需的字符串非常清楚.

  • **旁注**:在 Eclipse 编辑器中(至少在 Win10 上),当悬停“__DATE__”变量时,工具提示可能会显示类似:“Nov. 24 2021”,但是在运行程序时,字符串实际上是“2021 年 11 月 24 日”(所以没有点)。要**强制重新编译**(从而更新日期/时间),请删除分配了“__DATE__”的文件的“.o”文件,例如对于 Eclipse,在“项目设置”&gt;“C/C++ 构建”&gt;“设置”中&gt; 构建步骤 &gt; 预构建步骤 &gt; 命令 - 添加(不带引号,调整路径):``rm -f ${ProjDirPath}/Debug/Core/Src/version.o`` (4认同)
  • 在编译期间进行评估,只要表达式仅使用编译时已知的常量数据,如此处所示. (2认同)
  • **请注意**,仅当包含它的文件被重新编译时才会更新。更改其他一些源文件并重新链接项目不会导致日期被更新,如果您想要这样,您需要一个构建系统,它要么总是进行完全重新编译,要么有一个特殊的规则来始终强制重新编译包含此的源文件。 (2认同)

ste*_*eha 14

这是"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
Run Code Online (Sandbox Code Playgroud)

使用以下测试代码,上面的工作很棒:

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

但是,当我尝试将这些宏与您的字符串化宏一起使用时,它会将文字表达式字符串化!我不知道有什么办法让编译器将表达式减少为文字整数值然后进行字符串化.

此外,如果您尝试使用这些宏静态初始化值数组,编译器会抱怨一条error: initializer element is not constant消息.所以你不能用这些宏做你想做的事.

在这一点上,我认为你最好的选择是Python脚本,它只为你生成一个新的包含文件.您可以以任何所需的格式预先计算任何所需的内容.如果您不想要Python,我们可以编写AWK脚本甚至是C程序.


ste*_*eha 9

我给你一个部分答案.这是基于我从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
Run Code Online (Sandbox Code Playgroud)

我在Linux上用GCC测试了上面的内容.这一切都很好,除了我无法弄清楚如何得到一个月的数字的问题.如果您检查下面的部分,#if 0您将看到我试图找出月份.海湾合作委员会抱怨这条消息:

error: token ""Jul 27 2012"" is not valid in preprocessor expressions
Run Code Online (Sandbox Code Playgroud)

将三个字母的月份缩写转换为某种独特的数字是微不足道的; 只需从第一个字母中减去"A",从第二个和第三个字母中减去"a",然后转换为基数为26的数字或其他内容.但是我想让它在1月份评估为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'
};
Run Code Online (Sandbox Code Playgroud)

GCC抱怨"初始化元素不是常数" __DATE__.

对不起,我不知道该怎么帮助你.也许你可以用编译器尝试这些东西?或者它可能会给你一个想法.

祝好运.

PS如果你不需要数字,你只需要一个独特的构建字符串,这很简单:

const char *build_str = "Version: " VERSION " " __DATE__ " " __TIME__;
Run Code Online (Sandbox Code Playgroud)

使用GCC,结果如下:

Version: 1.4 Jul 27 2012 21:53:59
Run Code Online (Sandbox Code Playgroud)


ste*_*eha 8

您总是可以用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")
Run Code Online (Sandbox Code Playgroud)

我将所有定义都设为整数,但由于它们是简单的整数,因此如果您愿意,可以使用标准的字符串化技巧来构建字符串.此外,您可以简单地扩展它以构建其他预定义的字符串.

这个程序应该在Python 2.6或更高版本下运行,包括任何Python 3.x版本.您可以在旧的Python下运行它,只需进行一些更改,例如不使用.partition()解析字符串.


Dr *_*eco 5

简答(问版):(格式 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
Run Code Online (Sandbox Code Playgroud)

如果你不想要一个makefile更短的,只需编译:

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

简答(推荐版本):(格式 150710.182906)

使用double版本号:

制作文件:

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

或者一个简单的 bash 命令:

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

提示: 仍然不喜欢makefile还是只是为了一个不那么小的测试程序?添加这一行:

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

到您的~/.profile,并记住编译gcc $CPPFLAGS ...


长答案:

我知道这个问题比较老,但我要做出一点贡献。最佳实践总是自动化可能成为错误(或遗忘)的来源。

我习惯了一个为我创建版本号的函数。但我更喜欢这个函数返回一个float. 我的版本号可以通过以下方式打印:printf("%13.6f\n", version());它发出类似:(150710.150411年(2 位数)月日 DOT 小时分秒)。

但是,好吧,问题是你的。如果您更喜欢“major.minor.date.time”,它必须是一个字符串。(相信我,double 更好。如果你坚持主修,如果你设置主修并让小数为日期+时间,你仍然可以使用双精度,例如:major.datetime = 1.150710150411

让我们开始做生意。如果您像往常一样编译,忘记设置它,或者使用-DVERSION直接从 shell 设置版本,下面的示例将起作用,但更好的是,我推荐第三个选项:使用makefile.


三种编译形式及结果:

使用制作:

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
Run Code Online (Sandbox Code Playgroud)

跑步:

__DATE__: 'Jul 10 2015'
__TIME__: '04:58:29'
VERSION: '3.33.20150710.045829'
Run Code Online (Sandbox Code Playgroud)

使用 -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")\"
Run Code Online (Sandbox Code Playgroud)

跑步:

__DATE__: 'Jul 10 2015'
__TIME__: '04:58:37'
VERSION: '2.22.20150710.045837'
Run Code Online (Sandbox Code Playgroud)

使用内置函数:

beco> gcc program.c -o program.x -Wall -Wextra -g -O0 -ansi -pedantic-errors
Run Code Online (Sandbox Code Playgroud)

跑步:

__DATE__: 'Jul 10 2015'
__TIME__: '04:58:43'
VERSION(): '1.11.20150710.045843'
Run Code Online (Sandbox Code Playgroud)

源代码

  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
Run Code Online (Sandbox Code Playgroud)

请注意,字符串受MAJOR<=9999和限制MINOR<=9999。当然,我设置了这个希望永远不会溢出的高值。但使用double仍然是更好的(加,这是完全自动的,无需设置MAJOR,并MINOR通过手)。

现在,上面的程序有点太多了。更好的是完全删除该函数,并保证宏VERSION被定义,要么-DVERSION直接进入 GCC 命令行(或自动添加它以便您不会忘记的别名),要么推荐的解决方案,将此过程包含在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 $@
Run Code Online (Sandbox Code Playgroud)

DOUBLE 的更好版本

现在我向您展示了“您的”首选解决方案,这是我的解决方案:

直接用 (a) makefile 或 (b) gcc 编译:

(a) 生成文件:

   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 $@
Run Code Online (Sandbox Code Playgroud)

(b) 或者一个简单的 bash 命令:

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

源代码(双版):

#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;
}
Run Code Online (Sandbox Code Playgroud)

注意:只有在您忘记定义宏 VERSION 的情况下,才有这个双重功能。如果使用 amakefile或 set an alias gcc gcc -DVERSION=$(date +"%g%m%d.%H%M%S"),则可以安全地完全删除此功能。


嗯,就是这样。一种设置版本控制的非常简洁和简单的方法,再也不用担心了!