CMake:如果重建目标则重新生成源文件

CMake: Regenerate source file if target gets rebuilt

我正在尝试将构建日期嵌入到源文件中,这样每次构建特定目标时,都会刷新嵌入的日期,而不是每次构建整个项目时都重新生成。

即我有一个 header 文件 builddate.h,它是由具有一组 #define 的命令生成的。然后,此 header 文件包含在其他源文件中。

我的第一次尝试是这样的:

add_custom_target(builddate COMMAND <command that generates header file>)
add_library(mylibrary ...)
add_dependencies(mylibrary builddate)

这会正确生成 header 文件,但是每次都会生成 header 文件,无论 mylibrary 目标是否需要重建。

尝试使用自定义命令,即

add_custom_command(OUTPUT builddate.h COMMAND <command that generates header file>)
add_library(mylibrary ... builddate.h)

正确生成了一次 header,但是如果 mylibrary 目标被重建,header 不会重新生成,因为 builddate.h 已经是最新的。

这感觉应该是相当普遍的事情,但我无法弄清楚自定义命令和目标的什么样的咒语会给我想要的效果。我想要的是每次构建 mylibrary 目标时调用该命令,如果没有任何更改或构建不相关的目标(例如使用 mylibrary 的可执行文件),则不会进行虚假重建。

使用 PRE_BUILD 自定义命令听起来是个好主意,但文档指出,对于 Visual Studio 以外的生成器,它会在 PRE_LINK 命令之前被调用,即 after 源被编译。这似乎不适合此目的,因为编译源代码时需要 header。

https://cmake.org/pipermail/cmake/2010-October/040247.html 找到一个旧线程建议调用 CMake 的 --build 作为目标作为 PRE_LINK 命令:

# This is the library that I want to build
add_library(mylibrary ...)

# Set up a library that contains the code depending on the build date
# Use an OBJECT library because we don't need it to be a full static lib
# we just want to build some source that would "normally" have been part of mylibrary
add_library(builddate OBJECT EXCLUDE_FROM_ALL codethatusesbuilddate.cpp)

# Add a PRE_LINK command for mylibrary so that prior to linking we
# 1. Generate the builddate.h header
# 2. Call CMake to build the builddate library we just set up
add_custom_command(
    TARGET mylibrary PRE_LINK
    COMMAND <command that generates builddate.h>
    COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target builddate
)

# We also need to link with the library
# NOTE: uses the generator expression to link with the output files rather than the target
# to avoid CMake setting up a dependency from builddate to mylibrary which I think
# would cause builddate to be built prior to building mylibrary, but at that point we
# haven't generated the header yet. Which we could fix, but then we'd just build it twice
target_link_libraries(mylibrary PRIVATE $<TARGET_OBJECTS:builddate>)


这感觉有点别扭,但似乎可行。

脚注:生成 header 很容易使用 CMake 完成,即首先 configure_file 或类似的创建一个执行生成的 CMake 脚本,然后调用 ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/generated_cmake_file.cmake 作为命令生成 header.

前段时间写了一个cmake makro。它通过执行 Cversion.cmake 添加自定义命令以在当前构建目录中生成 version.cpp。 只有当依赖关系发生变化时才会执行文件的生成。 cmake-generator-expressions 依赖项设置为目标的依赖项减去它自己的(文件)。

可以通过添加 libs 依赖项来生成新的版本文件来改进它。

macro(add_versioning T)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/version.cpp"
        COMMAND ${CMAKE_COMMAND} "-DCMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
        -P "${PROJECT_SOURCE_DIR}/Version/CVersion.cmake"
        MAIN_DEPENDENCY "${PROJECT_SOURCE_DIR}/Version/version.cpp.in"
        DEPENDS "$<FILTER:$<TARGET_OBJECTS:${T}>,EXCLUDE,version.cpp.+$>")

target_include_directories(${T} PUBLIC "${PROJECT_SOURCE_DIR}/Version")
target_sources(${T} PUBLIC "${PROJECT_SOURCE_DIR}/Version/version.h" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/version.cpp")

endmacro()