C++ 生成可能更新或不更新的源文件
我有一个CMakeLists.txt,我想在其中生成几个源文件(即C++ 生成可能更新或不更新的源文件,c++,cmake,code-generation,C++,Cmake,Code Generation,我有一个CMakeLists.txt,我想在其中生成几个源文件(即versiondata.cpp和version.rc.inc,包括在res.rc)这取决于一般环境(当前git头,gcc-v输出,CMakeCache.txt本身,等等) 如果它仅仅依赖于一些文件,我将使用带有相关DEPENDS和OUTPUT子句的add_custom_命令指令生成它;然而,要精确定位它的文件依赖关系有点棘手;理想情况下,我希望在每次调用make时运行脚本,仅在需要时更新文件;如果生成的文件实际上已被触碰,则应重新
versiondata.cpp
和version.rc.inc
,包括在res.rc
)这取决于一般环境(当前git头,gcc-v
输出,CMakeCache.txt
本身,等等)
如果它仅仅依赖于一些文件,我将使用带有相关DEPENDS
和OUTPUT
子句的add_custom_命令
指令生成它;然而,要精确定位它的文件依赖关系有点棘手;理想情况下,我希望在每次调用make
时运行脚本,仅在需要时更新文件;如果生成的文件实际上已被触碰,则应重新生成依赖于它们的目标(如果这些文件的内容与以前相同,则脚本小心不要覆盖这些文件)
我的第一次尝试是使用带有假主输出的add_custom_命令
,如下所示:
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
从未真正生成,因此始终运行自定义命令。这工作正常,但总是重新生成我的可执行文件,因为CMake由于某些原因会自动接触输出文件(如果生成),即使我的脚本不处理它们
然后我想我可以使用一个自动“从未满足”的add\u custom\u target
,让它工作起来:
这里的想法是,versiondata
目标应该从依赖其副产品的目标中“拉入”,并且应该始终执行。这似乎适用于CMake 3.20,而且副产品
似乎有一些效果,因为如果我从我的可执行文件
中删除依赖项,我的脚本就不会被调用
然而,在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中,有两种类型的依赖关系:
目标级相关性,在目标之间
只有在无条件构建目标所依赖的所有目标后,才能构建目标
文件级相关性,在文件之间
如果某个文件比它的某个依赖项旧,则将使用相应的命令
重新生成该文件
关键因素是,在建立相关的目标之后,严格执行对相关文件的时间戳的检查
要正确地重新生成versiondata.cpp
文件和基于该文件的可执行文件,需要两个依赖项:
目标级别,这将确保versiondata
自定义目标
将在可执行文件之前生成
add_dependencies(my_executable versiondata)
文件级,这将确保在任何时候都重新生成可执行文件
将更新文件versiondata.cpp
通过列出versiondata.cpp
在可执行文件的源代码中
add_dependencies(my_executable versiondata)
现在谈谈副产品
即使没有明确的添加依赖项
,您的代码也可以在CMake 3.20上运行,因为副产品会自动生成所需的目标级依赖项
这可以从中的dependens
选项的描述中推断出来:
在版本3.16中更改:如果任何依赖项是同一目录中某个目标或其任何生成事件的副产品,则会添加目标级依赖项,以确保在生成此目标之前副产品可用
注意,add_executable
有效地依赖于它的每个源文件
因为给定的依赖项注释仅适用于CMake 3.16及更高版本,
在较旧的CMake版本中,副产品不会自动创建目标级别的依赖关系,需要求助于显式的add_dependencies
“我怀疑这在3.20中起作用只是偶然”——不,这种行为是完美的:因为CMake 3.16依赖于副产品会生成目标级别的依赖关系。在您的例子中,这是可执行目标my_executable
和自定义目标versiondata
之间的依赖关系。您可以通过add_dependencies(my_executable versiondata)
明确指定此依赖项,因此即使在CMake 3.16之前版本中,该构建也可以工作。与版本3兼容绝对没有任何好处。5@Tsyvarev如果是真的,很高兴知道这一点,尽管我不会说这是“完美的记录”:我想您引用的部分位于添加自定义目标
的依赖
部分下,正如本文所述,该部分适用于依赖于副产品的自定义目标,而不是其他类型的目标。如果这适用于一般目标,则应在IMO的副产品下。因此,明确的添加依赖项是3.16之前CMake的唯一方法?我很困惑,虽然有了CMake,我真的不应该这么做。@AlexReinking:而且,虽然我们已经做到了,但使用CMake代替一些更好的替代品也绝对没有什么好处,但我们现在就到了。有时有复杂的约束,你必须选择较小的邪恶,如果我特意测试一个特定的较旧的CMake版本,这可能就是其中之一。任何说你必须使用过时的CMake版本的约束都是假的,而且有点受虐狂。你走得越远,你就越会遇到像这样的荒谬问题,基本的事情没有按它们应该的方式工作,你浪费时间解决你根本不应该遇到的“问题”。