C++获取程序编译时间

C++
1346 0

使用C++语言编写的程序,如何获取程序的编译时间呢?

方式一、读取二进制程序文件的创建时间信息(不可靠)

编译时会修改二进制文件的“创建时间”信息,因此该时间可以看作程序编译的时间。

但是,如果将该程序文件复制、网络发送给其他机器,“创建时间”信息可能会发生改变。

方式二、使用 DATETIME

__DATE____TIME__宏是C/C++内置的宏,分别表示进行预处理时的日期和时间。

__DATE__会预处理为一个日期字符串,例如May 22 2023

__TIME__会预处理为一个时间字符串,例如01:38:46

首先定义一些宏,对__DATE____TIME__进行处理,提取年、月、日、时、分、秒,在此基础上转为unix时间戳(time_t)。

/**
 * @file build_date_time.h
 * @brief 处理宏 __DATE__ 和 __TIME__.
 * @author Leopard-C (leopard.c@outlook.com)
 * @version 0.1
 * @date 2023-05-22
 *
 * @copyright Copyright (c) 2023-present, Jinbao Chen.
 */
#ifndef IC_MACRO_BUILD_DATE_TIME_H_
#define IC_MACRO_BUILD_DATE_TIME_H_
#include <stdio.h>
#include <time.h>

 // Example of __DATE__ string: "May 22 2023"
 //                              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: "01:38:46"
//                              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])

// DateTime. Example: 2022-05-22 01:38:46
#define BUILD_DATE_TIME \
    {\
        BUILD_YEAR_CH0, BUILD_YEAR_CH1, BUILD_YEAR_CH2, BUILD_YEAR_CH3,\
        '-',\
        BUILD_MONTH_CH0, BUILD_MONTH_CH1,\
        '-',\
        BUILD_DAY_CH0, BUILD_DAY_CH1,\
        ' ',\
        BUILD_HOUR_CH0, BUILD_HOUR_CH1,\
        ':',\
        BUILD_MIN_CH0, BUILD_MIN_CH1,\
        ':',\
        BUILD_SEC_CH0, BUILD_SEC_CH1,\
        '\0'\
    }

extern const char g_build_date_time[];
extern time_t g_build_timestamp;

#endif // IC_MACRO_BUILD_DATE_TIME_H_
// build_date_time.cpp
#include "build_date_time.h"

static time_t get_build_timestamp() {
    struct tm tm_;
    tm_.tm_year = ((BUILD_YEAR_CH0 - '0') * 1000 + (BUILD_YEAR_CH1 - '0') * 100 + (BUILD_YEAR_CH2 - '0') * 10 + (BUILD_YEAR_CH3 - '0')) - 1900;
    tm_.tm_mon = ((BUILD_MONTH_CH0 - '0') * 10 + (BUILD_MONTH_CH1 - '0')) - 1;
    tm_.tm_mday = (BUILD_DAY_CH0 - '0') * 10 + (BUILD_DAY_CH1 - '0');
    tm_.tm_hour = (BUILD_HOUR_CH0 - '0') * 10 + (BUILD_HOUR_CH1 - '0');
    tm_.tm_min = (BUILD_MIN_CH0 - '0') * 10 + (BUILD_MIN_CH1 - '0');
    tm_.tm_sec = (BUILD_SEC_CH0 - '0') * 10 + (BUILD_SEC_CH1 - '0');
    tm_.tm_isdst = 0;
    return mktime(&tm_);
}

const char g_build_date_time[] = BUILD_DATE_TIME;
time_t g_build_timestamp = get_build_timestamp();

定义了两个全局变量 g_build_date_timeg_build_timestamp

使用示例:

// foo.cpp
#include "build_date_time.h"

void Foo() {
    std::cout << g_build_date_time << std::endl;  // 2022-05-22 01:38:46
    std::cout << g_build_timestamp << std::endl;  // 1653154726
}

现在还有一个问题,如何确保每次(增量/全量)编译的时候,都会更新这个时间呢?

一个可行的方法时,每次需要编译前,就更新build_date_time.cpp(也可以更新build_date_time.h)文件的时间戳,使该文件被重新编译。

(1) Visual Studio (Windows)

# 项目属性 -> 生成事件 -> 生成前事件 -> 命令行
# 注意:路径必须使用反斜杠
copy /b src\util\build_date_time.cpp +,, src\util\build_date_time.cpp

before_build

(2) makefile (Linux)

$(shell touch src/util/build_date_time.cpp)

before_build

(3) xmake

before_build(function (target)
    os.exec("touch src/util/build_date_time.cpp")
end)

END

Thanks for reading!

作者:iCrystal
邮箱:leopard.c@outlook.com
博客:https://blog.icrystal.top
GitHub: https://github.com/Leopard-C

最后更新 2023-05-28
评论 ( 0 )
OωO
隐私评论