web-dev-qa-db-ja.com

__DATE__および__TIME__の定義済みマクロを2つの整数として使用し、文字列化する方法

__ 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何か。

可能性のある解決策の1つは可能ですが、機能しませんでした: convert-date-to-unsigned-int

コンパイル時のコンテキストで convertint-date-and-time-string-to-just-integers-in-c 参照できます expanssion-and-stringification-how-to-get- the-marco-name-not-its-value

36
Rick2047

C++コンパイラを使用して、バージョン文字列を含めるオブジェクトファイルを作成できる場合は、必要な処理を正確に実行できます。ここでの唯一の魔法は、C++では式を使用して配列を静的に初期化できますが、Cではできないことです。式はコンパイル時に完全に計算可能である必要がありますが、これらの式はそうであるため、問題はありません。

バージョン文字列を一度に1バイトずつ作成し、必要なものを正確に取得します。

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

これは、あなたが要求した形式とは正確に異なりますが、整数に曜日と時間をマッピングする方法を完全に理解していません。これを作成して希望する文字列を作成する方法はかなり明確だと思います。

45
steveha

「ビルド定義」の作業バージョンは次のとおりです。これは以前の回答と似ていますが、ビルド月を把握しました。 (#ifステートメントでビルド月を計算することはできませんが、定数にコンパイルされる3項式を使用できます。)

また、ドキュメントによると、コンパイラが時刻を取得できない場合、これらの文字列に疑問符が付けられます。そこで、この場合のテストを追加し、これが発生した場合にさまざまなマクロが明らかに間違った値(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プログラムさえ書くことができます。

14
steveha

部分的な回答があります。これは、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

Linux上のGCCで上記をテストしました。その月の数字を取得する方法がわからないという問題を除いて、それはすべてうまくいきます。 #if 0の下にあるセクションをチェックすると、月を把握しようとする私の試みが表示されます。 GCCは次のメッセージで苦情を言っています。

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

3文字の月の省略形をある種の一意の数字に変換するのは簡単です。最初の文字から「A」を、2番目と3番目の文字から「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'
};

GCCは__DATE__に対して「初期化子要素は定数ではない」と不平を言っています。

申し訳ありませんが、私はあなたを助ける方法がわかりません。たぶん、あなたはコンパイラでこのようなことを試すことができますか?または、アイデアが得られるかもしれません。

がんばろう。

追伸数値にする必要はなく、一意のビルド文字列が必要な場合は簡単です。

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

GCCでは、次のような結果になります。

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

Pythonまたは何かで簡単なプログラムを作成して、ビルド番号、時刻、日付を含む簡単な_#define_ステートメントを含むインクルードファイルを作成できます。その後、ビルドを行う前にこのプログラムを実行する必要があります。

必要に応じて、ここに1つ書いてソースを投稿します。

運が良ければ、ビルドツール(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 3.xバージョンを含むPython 2.6以降で正常に動作するはずです。文字列の解析に.partition()を使用しないなど、いくつかの変更を加えて古いPythonの下で実行できます。

8
steveha

短い回答(質問バージョン):(format 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")
_

Tip:それでも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_を使用してシェルから直接バージョンを設定する場合に機能しますが、何よりも、3番目のオプション:makefileを使用することをお勧めします。


コンパイルの3つの形式と結果:

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を使用したより良いバージョン

「お好みの」ソリューションを紹介したので、ここに私のソリューションを示します。

(a)makefileまたは(b)gccで直接コンパイル:

(a)MakeFile:

_   VERSION = $(Shell date +"%g%m%d.%H%M%S")
   CC = gcc
   CFLAGS = -Wall -Wextra -g -O0 -ansi -pedantic-errors 
   CPPFLAGS = -DVERSION=$(VERSION)
   LDLIBS =
   %.x : %.c
         $(CC) $(CFLAGS) $(CPPFLAGS) $(LDLIBS) $^ -o $@
_

(b)または単純な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")を設定すると、この関数を安全に完全に削除できます。


まあ、それだけです。バージョン管理をセットアップする非常にきちんとした簡単な方法であり、それについて二度と心配する必要はありません!

4
Dr Beco

日が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] }

出力:2019年9月6日

0
hueman

とても簡単です。

[メイクファイル内]

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

OBJS = .... \

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

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

DATE = $(Shell date + 'char szVersionStr [20] = "%Y-%m-%d%H:%M:%S";')<< == add

all:version $(ProgramID)<< ==最初にバージョンを追加

バージョン:<< == add

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

[プログラム内]

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

extern char szVersionStr [20];

[使用]

=== 4 ====

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