c++ - 生成可能更新也可能不更新的源文件

标签 c++ cmake code-generation

我有一个 CMakeLists.txt,我想在其中生成几个源文件(即 versiondata.cppversion.rc.inc,包含在 中res.rc) 取决于一般环境(当前 git HEAD、gcc -v 输出、CMakeCache.txt 本身,等等)。

如果它仅依赖于某些文件,我会使用带有相关DEPENDSOUTPUT 子句的add_custom_command 指令生成它;然而,要准确定位它的文件 依赖关系有点棘手;理想情况下,我希望每次调用 make 时都运行我的脚本,仅在需要时更新文件;如果生成的文件实际上已被触及,则应重建依赖于它们的目标(如果文件与以前的内容相同,脚本会小心不要覆盖文件)。

我的第一次尝试是使用带有伪主输出的 add_custom_command,如下所示:

add_custom_command(OUTPUT versiondata.cpp.fake versiondata.cpp version.rc.inc
    COMMAND my_command my_options
    COMMENT "Generating versiondata.cpp"
)
# ...
# explicitly set the dependencies of res.rc, as they are not auto-deduced
set_source_files_properties(res.rc PROPERTIES OBJECT_DEPENDS "${PROJECT_BINARY_DIR}/version.rc.inc;${PROJECT_SOURCE_DIR}/other_stuff.ico")
# ...
add_executable(my_executable WIN32 ALL main.cpp versiondata.cpp res.rc)

versiondata.cpp.fake 从未真正生成,因此始终运行自定义命令。这工作正常,但总是重建 my_executable,因为出于某些原因 CMake 会自动接触输出文件(如果生成),即使我的脚本让它们单独存在也是如此。

然后我想我可以使用 add_custom_target 让它工作,它会自动“从未满足”:

add_custom_target(versiondata BYPRODUCTS versiondata.cpp version.rc.inc
    COMMAND my_command my_options
    COMMENT "Generating versiondata.cpp"
)
# ...
# explicitly set the dependencies of res.rc, as they are not auto-deduced
set_source_files_properties(res.rc PROPERTIES OBJECT_DEPENDS "${PROJECT_BINARY_DIR}/version.rc.inc;${PROJECT_SOURCE_DIR}/other_stuff.ico")
# ...
add_executable(my_executable WIN32 ALL main.cpp versiondata.cpp res.rc)

这里的想法是 versiondata 目标应该从依赖于它的 BYPRODUCTS 的目标中“拉入”,并且应该始终执行。这似乎适用于 CMake 3.20,并且 BYPRODUCTS 似乎有一些影响,因为如果我从 my_executable 中删除依赖项,我的脚本就不会被调用。

但是,在 CMake 3.5 上我得到了

make[2]: *** No rule to make target 'version.rc.inc', needed by 'CMakeFiles/my_executable.dir/res.rc.res'.  Stop.

如果我从 version.rc.inc 中删除显式依赖项,它根本不会生成

[ 45%] Building RC object CMakeFiles/my_executable.dir/res.rc.res
/co/my_executable/res.rc:386:26: fatal error: version.rc.inc: No such file or directory
 #include "version.rc.inc"
                          ^
compilation terminated.
/opt/mingw32-dw2/bin/i686-w64-mingw32-windres: preprocessing failed.
CMakeFiles/my_executable.dir/build.make:5080: recipe for target 'CMakeFiles/my_executable.dir/res.rc.res' failed
make[2]: *** [CMakeFiles/my_executable.dir/res.rc.res] Error 1

所以我怀疑这在 3.20 中起作用的事实只是偶然的。

长话短说:有什么方法可以让这项工作如我所愿?

最佳答案

在 CMake 中有两种类型的依赖:

  1. 目标之间的目标级依赖。

    目标只有在无条件构建它所依赖的所有目标后才能构建。

  2. 文件之间的文件级依赖。

    如果某个文件比其依赖项之一旧,将使用相应的 COMMAND 重新生成该文件。

关键因素是依赖文件的时间戳检查是严格构建依赖目标之后执行的。

为了正确地重新生成 versiondata.cpp 文件和基于它的可执行文件,需要两个依赖项:

  1. 目标级别,这将确保 versiondata 自定义目标 将在可执行文件之前构建。

    add_dependencies(my_executable versiondata)
    
  2. 文件级别,这将确保可执行文件将在任何时候重建 文件 versiondata.cpp 将被更新。

    此依赖项是通过列出 versiondata.cpp 自动创建的 在可执行文件的来源中。


现在谈谈副产品。

即使没有明确的 add_dependencies,您的代码也可以在 CMake 3.20 上运行,因为副产品会自动生成所需的目标级依赖项。

这可以从 add_custom_target 中的 DEPENDS 选项的描述中推导出来/add_custom_command :

Changed in version 3.16: A target-level dependency is added if any dependency is a byproduct of a target or any of its build events in the same directory to ensure the byproducts will be available before this target is built.

并注意,add_executable 实际上依赖于它的每个源文件。

因为对 DEPENDS 的注释仅适用于 CMake 3.16 及更高版本, 在较旧的 CMake 版本中,BYPRODUCTS 不会自动创建目标级依赖项,需要求助于显式add_dependencies

关于c++ - 生成可能更新也可能不更新的源文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67907927/

相关文章:

c++ - 无限的 typedef 函数指针继承可能吗?

escaping - 为什么 CMake 在执行命令时在空格前加上反斜杠?

c++ - 在 CMake header-only library 中生成 "stores"它对 boost 文件系统的依赖

c# - 格式化 C# 代码片段的文字参数

parsing - 我想写一个编译器;你推荐什么程序集(x86)/后端?是否使用 BISON?

c++ - std::forward Visual Studio 13 的行为与我预期的不同

c++ - Linux 下 C++ 音频捕获 API 的最佳实践?

C++ Vector 下标超出范围(但事实并非如此)

c++ - 如何在 CMake 中检查 Windows 版本?

namespaces - Enterprise Architect 代码生成模板 : import hierarchical package structure as a string