如何在命令输出更改时触发CMake重新配置

6
我希望能够在命令输出改变时触发CMake配置;具体来说,我想让CMake在git describe --always --dirty的输出与上一次配置CMake不同时进行配置。
大部分问题可以通过监视HEAD文件并解析其中的symref到refs/heads/[branch],然后使用configure_file(...)将这些链接起来来解决,但是这不能检测到树处于脏状态时(即存在未提交的修改)。在这种情况下,git describe --always --dirty会在输出中添加-dirty后缀。
当出现这种情况时,git文件没有任何更改,只是git注意到与存储状态的差异,因此我无法在这里对任何文件进行configure_file(...)以使CMake注意到更改并重新配置。
所以我想知道是否有办法让CMake运行git命令,注意到输出的差异并触发重新配置,这几乎需要类似于预重新配置检查阶段的东西。
不确定是否可能实现或是否有任何其他方法可以实现这种行为?

不要在CMake本身中执行此类操作,因为这会导致一种“鸡生蛋”的问题(当没有文件更改时检查更改的文件)。只需将其移动到某个shell脚本包装器中,调用您的git、配置和构建步骤即可。 - Florian
因此,我这样做的原因是为了将git describe字符串编译到版本信息中,以确保我知道它是从哪个git提交版本构建的,并且至关重要的是,如果有来自该提交的修改。如果可以避免,我不想在其上添加元构建系统,仅仅为了获得那一个小功能片段,我也不明白为什么它会陷入鸡生蛋或蛋生鸡的局面? - rblk
1
我不知道如何强制CMake在构建过程中重新运行配置。但是,如果您只想更改version.h并重新构建与其相关的所有内容,您可以在build阶段使用类似add_custom_target(BuildVersion COMMAND ${CMAKE_EXECUTABLE} -P <script_file>)的方式生成该文件。这会以脚本模式运行CMake,您可以在其中使用常规的configure_file命令。进一步,使用add_dependencies(<exec-target> BuildVersion)命令将确保如果脚本更改了version.h文件,则可执行文件将被重新构建。 - Tsyvarev
这个相关的问题可能会有所帮助(还要阅读该答案的评论)。 - Craig Scott
这里有相关的讨论和解决方案:https://dev59.com/b3M_5IYBdhLWcg3wTRPL - Mark Dewing
1个回答

3

问题

这里的问题是,“预重新配置检查阶段”由您的构建环境处理。只有在更改了它的某些输入文件时,才会调用 CMake(因为 CMake 生成了规则来跟踪这些文件是否已被更改)。

在本讨论中,让我们假设您有类似以下内容的东西:

execute_process(
    COMMAND ${GIT_EXECUTABLE} describe --always --dirty
    OUTPUT_VARIABLE _git_file_list
    OUTPUT_STRIP_TRAILING_WHITESPACE
)

configure_file(version.h.in version.h)

外部命令输出更改时不支持内置重新配置

我认为,如果没有将额外的脚本作为构建过程的一部分,很难像“将外部命令输出作为预重新配置步骤进行检查”这样的操作。

你可以通过在实际构建之前调用make rebuild_cache或添加add_custom_command(TARGET MyExe POST_BUILD ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/version.h)来强制使用CMake每次重新配置,但是每次都调用配置过程非常耗时。

解决方案

在我的项目中,我将像上面那样的代码移动到自己的WriteVersionInfo.cmake脚本中:

set(_version_cpp_full_path "${CMAKE_CURRENT_BINARY_DIR}/version.cpp")
set(_version_obj_full_path "${_version_cpp_full_path}${CMAKE_CXX_OUTPUT_EXTENSION}")

string(REPLACE " " ";" _compiler_flags_list ${CMAKE_CXX_FLAGS})

# Create a dummy output to satisfy dependency check
if (NOT EXISTS ${_version_obj_full_path})
    file(WRITE ${_version_obj_full_path} "")
endif()

add_custom_command(
    TARGET MyExe
    PRE_LINK 
    COMMAND ${CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE} 
                             -D _version_cpp_name=${_version_cpp_full_path} 
                             -P ${CMAKE_CURRENT_SOURCE_DIR}/WriteVersionInfo.cmake
    COMMAND ${CMAKE_CXX_COMPILER} ${_compiler_flags_list} 
                                  -o ${_version_obj_full_path} 
                                  -c ${_version_cpp_full_path}
    VERBATIM
)

target_link_libraries(
    MyExe
    ${_version_obj_full_path}
)

这里我直接写入一个 .cpp 文件,并将其直接作为 PRE_LINK 步骤传递给编译器。请注意,上述方法不适用于库目标(因为没有预链接步骤)。

参考文献


就我所读的手册来看,PRE_LINK仅适用于库和可执行文件,而不适用于add_custom_target() - Victor Sergienko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接