当生成器或输入在 CMake 中发生变化时,如何仅构建自动生成的代码?
我正在开发一个源代码存储库,该存储库通过运行输出标头和实现的 python 脚本生成一些 C++ 代码.此代码随后被编译并链接到我的库和可执行文件.我知道生成的代码只有在两个条件之一为真时才会改变:
I am working on a source code repository that generates some C++ code by running a python script outputting headers and implementation. This code is subsequently compiled and linked to my libraries and executables. I know that the generated code will only change if one of two conditions are true:
- 生成器代码本身发生了变化
- 生成器的输入(一个 XML 文件)发生变化
我想使用 cmake 来管理构建过程.目前,我正在使用 execute_process
来触发生成器.但是,每次我运行 cmake 时都会运行它并且它会触及文件,导致我生成的代码被重新编译并增加了我的总编译时间.
I want to use cmake to manage the build process. At the moment, I am using execute_process
to fire off the generator. However, this runs every time I run cmake and it touches the files, causing my generated code to be recompiled and adding to my total compile time.
我还想确保生成的代码始终在我的库之前运行.换句话说,我希望库依赖于生成器运行.
I also want to make sure that the generated code is always run before my libraries. In other words, I want the libraries to depend on the generator to have run.
在 cmake 中处理这种情况的正确方法是什么?我看过以前的答案:在构建库之前让 CMake 执行项目中的目标".但这依赖于预先知道的代码生成器的输出.我的代码生成器将生成可变数量的文件.
What is the proper way to handle such a situation in cmake? I have seen this previous answer: "Get CMake to execute a target in project before building a library". But this relies on the output of the code generator being known in advance. My code generator will generate a variable number of files.
推荐答案
使用 ADD_CUSTOM_COMMAND
来触发你的生成器.它允许您定义输入和输出依赖关系,并且只有在输出比输入更旧时才会运行.
Use ADD_CUSTOM_COMMAND
to trigger your generator. It allows you to define input and output dependencies and will only run if the outputs are older than the inputs.
ADD_CUSTOM_COMMAND( OUTPUT generatedfile1 generatedfile2
COMMAND python generateSources.py xmlfile1 xmlfile2
DEPENDS xmlfile1 xmlfile2 generateSources.py
COMMENT "Generating source code from XML" )
确保生成的文件不会在多个可能并行编译的独立目标中使用,否则您可能(将)在构建过程中遇到冲突.为了确保这一点,以下内容应该可以解决问题:
Make sure that the generated files are not used in more than one independent target that may compile in parallel or you may(will) get a conflict during your build. To ensure this, the following should do the trick:
ADD_CUSTOM_TARGET( RunGenerator DEPENDS generatedfile1 generatedfile2
COMMENT "Checking if re-generation is required" )
然后让你的其他目标依赖于这个:
Then make your other targets depend on this one:
ADD_DEPENDENCIES( MyTarget RunGenerator )
注意: RunGenerator
目标将始终被视为过时,因此始终运行.但是,因为在这种情况下它什么都不做(除了打印注释和检查依赖项),所以没有关系.如果需要,自定义命令将负责重新生成.
NB: The RunGenerator
target will always be considered out-of-date and, thus, always run. However, since it does nothing (besides printing the comment and checking the dependencies) in this case, that doesn't matter. The custom command will take care of regeneration IF required.
评论后更新:
如果不知道文件名,可以使用
If you do not know the name of the files, you can use
ADD_CUSTOM_COMMAND( OUTPUT generated.timestamp
COMMAND python generateSources.py xmlfile1 xmlfile2
COMMAND ${CMAKE_COMMAND} -E touch generated.timestamp
DEPENDS xmlfile1 xmlfile2 generateSources.py
COMMENT "Generating source code from XML" )
但是:使用 GLOB 需要您显式运行 CMake 来更新您的文件列表.将此集成到自定义命令中可能会扰乱您的构建过程(如果多个项目正在并行构建并且一个项目重新启动 CMake 配置).IIRC,当您知道 python 脚本或 XML 文件发生更改时,您可以手动运行 CMake,但您的问题是当其他任何事情需要重新运行 CMake 时,这些文件会被触及.
However: Using GLOB requires you to explicitly run CMake to update your file lists. Integrating this into the custom command would probably mess up your build process (if several projects are building in parallel and one project restarts CMake configuration). IIRC, it is ok for you to run CMake manually when you know that either the python script or the XML files changed but your problem is that those files are touched when anything else requires a re-run of CMake.
如果 python 脚本运行时间不长,你可以让它在每次 CMake 运行时运行(就像你现在做的那样),但要确保未更改的文件不会被触及,你可以尝试以下(未经测试):
If the python script does not take too long to run, you could let it run with each CMake run (like you do now) but make sure that the unchanged files do not get touched, you can try the following (untested):
# generated sources files into a temporary directory (adjust your current execute_process)
EXECUTE_PROCESS( COMMAND python ../generateSources.py ../xmlfile1 ../xmlfile2
WORKING_DIRECTORY tmp )
# get the filenames
FILE( GLOB GENERATED_TEMP_FILES tmp/* )
# copy to the "expected" directory, but only if content CHANGED
FOREACH( F ${GENERATED_TEMP_FILES} )
GET_FILENAME_COMPONENT( "${F}" FN NAME)
CONFIGURE_FILE( "${F}" "./generated/${FN}" COPY_ONLY )
ENDFOREACH()
# use your current globbing command
FILE( GLOB GENERATED_SOURCES ./generated/* )
相关文章