【问题标题】:Compile date and time using cmake使用 cmake 编译日期和时间
【发布时间】:2014-06-18 18:44:09
【问题描述】:

如果是发布版本,我想使用 cmake 将 VERSION 设置为发布版本,否则设置为编译时间。

使用 make 进行开发构建时,通过以下方式轻松获取编译时间

-DVERSION=`date +%Y-%m-%d_%H:%M`

可以直接被 c/c++ 源代码使用。不幸的是,我还没有发现使用 cmake 时如何实现相同的效果。

string(TIMESTAMP VERSION "%Y-%m-%d %H:%M")
add_definitions(-DVERSION="${VERSION}")

将 VERSION 设置为执行 cmake 的时间。如何在使用 cmake 时将 VERSION 设置为编译时间(以避免在没有 RELEASE 标志的情况下不得不摆弄__DATE____TIME__)?

【问题讨论】:

  • Here's a useful answer 解决您的问题。您需要确保您的主机构建环境中有一些可用的命令,例如date。使用__DATE____TIME__ 宏的通用解决方案也很容易,只需提供一个包含必要定义的头文件,该文件在每次构建时生成(或至少触及)(作为预构建操作)跨度>
  • 一般来说,版本号的目的是能够重现构建二进制文件的确切源文件。因此,使用编译的时间戳是一个非常、非常糟糕的选择。分配并维护正确的版本号,或者如果您不想这样做,请使用您的版本控制软件中的某些内容(SVN 的分支/修订,GIT 的哈希码)。只是不要使用编译时间戳,这比根本没有版本更糟糕,因为你可以对旧源进行更新编译,这会让包括你自己在内的每个人都感到困惑。
  • @DevSolar 我认为您在这里混合了不同的想法。修订的目的,而不是版本,是为了能够重现确切的源文件。构建 ID 的目的是能够在我们的构建/开发机器上找到准确的构建工件。 app/lib 版本的目的其实很广泛,从营销目的开始,到后向兼容性问题结束。 app/lib 版本可能包含也可能不包含构建 ID 或修订版。如何跟踪特定版本以构建或修订取决于开发人员(操作人员)。至于时间戳,它可以很好地用作开发构建的构建 ID。
  • @AlexChe:时间戳构建 ID 的问题是时间戳不允许将二进制文件追溯到其源,除非您仅使用单个代码分支 - 即使那样您也会必须摆弄您正在使用的 VCS。 VCS 修订版更好,但仍然会失去其信息价值,例如必须(或想要)迁移您的 VCS。恕我直言,您应该有一个产品版本、组件版本,并且理想情况下使用一些特定于 VCS 的方式来标记进入组件构建的源,如果需要,您可以迁移到不同的 VCS。

标签: c++ c cmake


【解决方案1】:

对于相对较新的 CMake 版本 (>=2.8.11):

string(TIMESTAMP {output variable} [{format string}] [UTC])

(请参阅http://www.cmake.org/cmake/help/v3.0/command/string.html)。例如:

string(TIMESTAMP TODAY "%Y%m%d")

【讨论】:

  • 你能补充几个例子吗?
  • string(TIMESTAMP ... 被缓存,直到您编辑 CMakeLists.txt
【解决方案2】:

我在第一次运行 CMake 时的跨平台解决方案在二进制目录中创建了一个文件 timestamp.cmake,并定义了一个运行生成文件的目标 timestamp。文件timestamp.cmake 使用STRING CMake 命令形成一个ISO 8601 时间戳字符串,并将其写入文件timestamp.h,并在前面加上#define _TIMEZ_ 预处理器指令(使用一个前导下划线定义是可以的;使用两个前导下划线定义不应是用户定义的)。

在您的主 CMake 文件中包含以下内容。

# build time in UTC ISO 8601
FILE (WRITE ${CMAKE_BINARY_DIR}/timestamp.cmake "STRING(TIMESTAMP TIMEZ UTC)\n")
FILE (APPEND ${CMAKE_BINARY_DIR}/timestamp.cmake "FILE(WRITE timestamp.h \"#ifndef TIMESTAMP_H\\n\")\n")
FILE (APPEND ${CMAKE_BINARY_DIR}/timestamp.cmake "FILE(APPEND timestamp.h \"#define TIMESTAMP_H\\n\\n\")\n")
FILE (APPEND ${CMAKE_BINARY_DIR}/timestamp.cmake "FILE(APPEND timestamp.h \"#define _TIMEZ_ \\\"\${TIMEZ}\\\"\\n\\n\")\n")
FILE (APPEND ${CMAKE_BINARY_DIR}/timestamp.cmake "FILE(APPEND timestamp.h \"#endif // TIMESTAMP_H\\n\")\n")
ADD_CUSTOM_TARGET (
    timestamp
    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/timestamp.cmake
    ADD_DEPENDENCIES ${CMAKE_BINARY_DIR}/timestamp.cmake)

然后使用ADD_DEPENDENCIES CMake 命令使您的主目标(可能是主可执行文件)依赖于timestamp 目标。 CMake 始终认为它已过时,因此每次主目标重新构建时都会刷新它,并根据要求刷新构建时间。

ADD_DEPENDENCIES (${CMAKE_BINARY_DIR}/${BINARY_NAME} timestamp)

如果需要,您可以使用此命令指定多个附加依赖项,用空格分隔。

然后您可以只使用#include "timestamp.h"(假设 CMake 二进制目录位于包含路径中,通常是。如果没有,那很简单:INCLUDE_DIRECTORIES (${CMAKE_BINARY_DIR})),并在您想要构建时使用_TIMEZ_ ISO 8601 格式的时间戳(或者,事实上,任何你喜欢的格式:你可以自己指定,see CMake documentation for STRING command usage)。

这可以通过直接(手动)创建文件timestamp.cmake 并将其添加到您的代码存储库来简化,但我认为它不够干净。这是 CMake 的一个普遍缺点,您无法在 CMake 的后端(例如,GNU make)运行的阶段访问时间戳字符串形成过程(STRING CMake 命令中使用的过程),因此必须使用单独的 CMake 文件并在该阶段调用它。如果您可以在“CMake 命令模式”(cmake -E 调用类型)中调用 CMake 时间戳字符串形成过程,这可以更简单、更清晰地完成,例如:cmake -E date [format] [UTC],但唉.我有filed a ticket in the CMake's Mantis bug tracker

您可以通过支持我的功能请求来帮助实现这一点,张贴一些 cmets 显示您对它的需要程度。

【讨论】:

  • 用前导下划线然后定义大写字母其实是不行的。 (并不是说这太重要了。)
  • (这是旧的,但是)据我了解,以前导下划线开头的 所有(无范围)符号都保留给实现。
【解决方案3】:

也许您可以在代码中使用编译器宏 __DATE__ __TIME__ 而不是从 cmake 获取它。值得一提的是,您需要执行 clean/make 来更新这些值(因为 GCC 嵌入了它,如果对象已经编译,它将不会再次编译,因此不会更改日期/时间)

【讨论】:

  • 这是一个合理的建议,但它的缺点是会阻止完全可重现的构建,因为 __DATE____TIME__ 会根据编译时间而改变。相反,在编译时将这些值作为不同的宏传递可能会更好。
  • 几年后,我真的同意你的看法。使用 DATETIME 将无法从特定版本创建 1:1 二进制文件。
【解决方案4】:

我最终得到以下解决方案:

if(CMAKE_SIZEOF_VOID_P EQUAL 8)
    add_custom_target(
        "linktimestamp"
        ALL
        COMMAND date +'%Y-%m-%d %H:%M:%S' > "linktimestamp.txt"
        COMMAND objcopy --input binary --output elf64-x86-64 --binary-architecture i386:x86-64 --rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA "linktimestamp.txt" "linktimestamp.o"
        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
        COMMENT "link timestamp: ${LINK_TIMESTAMP}"
        )
else()
    add_custom_target(
        "linktimestamp"
        ALL
        COMMAND date +'%Y-%m-%d %H:%M:%S' > "linktimestamp.txt"
        COMMAND objcopy --input binary --output elf32-i386 --binary-architecture i386 --rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA "linktimestamp.txt" "linktimestamp.o"
        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
        COMMENT "link timestamp: ${LINK_TIMESTAMP}"
        )
endif()
#add_dependencies(${PROJECT_NAME} "linktimestamp")
target_link_libraries(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/linktimestamp.o")

二进制目标${PROJECT_NAME} 每次都与更新的部分链接。

Qt 获取此时间戳的代码:

extern char _binary_linktimestamp_txt_start[];
//extern char _binary_linktimestamp_txt_end[];
extern char _binary_linktimestamp_txt_size[];
const auto text = QByteArray::fromRawData(_binary_linktimestamp_txt_start, reinterpret_cast< std::intptr_t >(_binary_linktimestamp_txt_size));
qDebug() << QDateTime::fromString(QString::fromUtf8(text), "yyyy-MM-dd HH:mm:ss\n"));

【讨论】:

    【解决方案5】:

    我不久前遇到了同样的问题,最后写了this repo 作为解决方案。只需将其添加为 CMake 子目录,build_date_str 将包含您的程序的链接时间

    如果github在stackoverflow之前就下线了,这个解决方案的思路是:

    1. 如上述解决方案一样,使用string(TIMESTAMP ... 创建一个BUILD_TIME CMake 变量。
    2. 编译一个小型库,将BUILD_TIME 从您的程序中记录到一个全局变量(您可以打印或其他)中。
    3. 使该库的构建依赖于自定义目标,该目标使用 cmake -U BUILD_TIME 从 CMake 缓存中删除变量。

    这意味着每次构建项目时,都会检查依赖项,并清除 CMake 缓存,从而导致为该时间戳库重新运行 CMake。 这个解决方案的一个缺点是,如果你有一个 CI 系统,你需要记住传递给你的 CMake 步骤的任何参数也传递给你的构建步骤。

    【讨论】:

      猜你喜欢
      • 2010-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多