如何仅在生成器或输入在CMake中发生更改时构建自动生成的代码
How to only build auto generated code when the generator or input changes in CMake?
我正在开发一个源代码存储库,该存储库通过运行输出标头和实现的python脚本来生成一些C++代码。此代码随后被编译并链接到我的库和可执行文件。我知道生成的代码只有在两个条件之一为真时才会改变:
- 生成器代码本身发生更改
- 生成器的输入(XML文件)发生更改
我想使用cmake来管理构建过程。目前,我正在使用execute_process
来启动发电机。然而,每当我运行cmake时,它就会运行,并且它会接触到文件,导致我生成的代码被重新编译,并增加了我的总编译时间。
我还想确保生成的代码总是在我的库之前运行。换句话说,我希望库依赖于生成器来运行。
在cmake,处理这种情况的正确方法是什么?我以前看到过这样的回答:"在构建库之前,让CMake执行项目中的目标"。但这依赖于代码生成器的输出是事先已知的。我的代码生成器将生成数量可变的文件。
使用ADD_CUSTOM_COMMAND
来触发生成器。它允许您定义输入和输出依赖关系,并且只有在输出比输入旧时才会运行。
ADD_CUSTOM_COMMAND( OUTPUT generatedfile1 generatedfile2
COMMAND python generateSources.py xmlfile1 xmlfile2
DEPENDS xmlfile1 xmlfile2 generateSources.py
COMMENT "Generating source code from XML" )
请确保生成的文件不会在多个独立的目标中使用,这些目标可能会并行编译,或者在构建过程中可能会发生冲突。为了确保这一点,以下应该做到:
ADD_CUSTOM_TARGET( RunGenerator DEPENDS generatedfile1 generatedfile2
COMMENT "Checking if re-generation is required" )
然后让你的其他目标依赖于这个:
ADD_DEPENDENCIES( MyTarget RunGenerator )
注意:RunGenerator
目标将始终被视为过时,因此始终运行。然而,由于在这种情况下它什么都不做(除了打印注释和检查依赖项),所以这无关紧要。如果需要,自定义命令将负责再生。
评论后更新:
如果你不知道文件的名称,你可以使用
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时,这些文件都会被触及。
如果python脚本运行时间不长,您可以让它在每次CMake运行时运行(就像现在一样),但要确保未更改的文件不会被触摸,您可以尝试以下操作(未经测试):
# 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/* )
一个低级别的解决方案在另一个目录中生成文件,将文件与当前文件进行比较,如果它们不同,只进行复制,不进行复制,也不重新编译。
当然,只有当生成器没有填充一些随机噪声(如版本和日期)时,这才起作用。在这种情况下,你可以试着把它们过滤掉。
我今天或多或少遇到了这个问题。我正在将二进制资源嵌入到c++可执行文件中(它是一个嵌入式web服务器)。
解决方法如下:
#get the file timestamp of the 'source' resource file
FILE(TIMESTAMP "${CMAKE_CURRENT_SOURCE_DIR}/${_resource}" RESOURCE_TIME)
#get the file time of the 'template' file
FILE(TIMESTAMP ${TEMPLATE_FILE} TEMPLATE_TIME)
#get the timestamp (if any) of the generated file
FILE(TIMESTAMP "${CMAKE_CURRENT_BINARY_DIR}/${_filename}" TARGET_TIME)
....
#only configure the file if the target is older than either the
#source or the template
IF((RESOURCE_TIME > TARGET_TIME) OR (TEMPLATE_TIME > TARGET_TIME) OR (NOT TARGET_TIME))
MESSAGE(STATUS "configuring ${...}")
FILE(READ ${_resource} RESOURCE_CONTENT HEX)
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\1," RESOURCE_CONTENT ${RESOURCE_CONTENT})
configure_file("${TEMPLATE_FILE}"
"${CMAKE_CURRENT_BINARY_DIR}/${_filename}"
@ONLY)
ELSE()
MESSAGE(STATUS "already configured ${...}")
ENDIF()
我工作的地方也遇到过类似的问题。只是我们不使用cmake,而是使用另一个构建系统。
我不知道cmake,但这可能会给你几个想法:
我们的解决方案包括将xml和生成器视为需要编译的代码,将生成器视为依赖项,将xml文件视为源。如果生成器或xml文件发生了更改,则构建系统会运行生成器(生成所有内容——即使只有一个文件发生了变化,也意味着它会触及所有生成的文件)。
当然,构建系统并没有直接运行生成器,而是我们编写了一个小的python脚本,决定如何正确运行生成器——例如,我们可以添加的一个改进是将所有文件生成到/tmp,并且只移动更改后的文件(使用diff进行比较),因此只有更改过的文件才会被触摸。(我们现在不需要它,因为我们的文件不会经常更改)
我们还使用两个不同的图运行了两次构建系统,一个图用于生成器,另一个图则用于其他文件。我们将其设计为允许多个级别的生成器生成依赖关系,以便一个生成器可以依赖于另一个生成器生成的产品。
另外两个需要考虑的技巧是,如果您的生成系统允许您使用正则表达式来生成文件,您可能需要使用它。此外,您可以在生成过程中生成生成系统的配置文件。
- Clion显示错误,但可以使用Cmake成功构建代码
- 使用 cl 构建代码并连接到 sqlite 库
- 在 Azure DevOps 构建管道中使用英特尔C++编译器为 Linux 环境构建C++代码
- 可以在没有构建代码的情况下转到定义吗?
- 在visual Studio中构建代码时,我遇到错误,.h文件丢失
- 如何使用提升库构建 C++ 代码
- 构建代码时出错
- 构建 C/C++ 代码时的 Gradle 编译器输出
- 如何在Microsoft Visual Studio之外构建C 代码
- 如何使用选择正确检测我是在Windows还是Linux中构建C 代码
- 可以在Visual Studio中使用Bazel构建代码
- Cmake并需要两次运行才能成功构建代码
- 无法构建代码块史密斯wxWidget项目
- g++ 使用 Sublime Text 2 构建C++代码时的致命错误
- 在Visual C++Express中构建C代码
- Cython是用于构建C代码还是用于构建Python扩展?
- g++ 4.6.3在64位服务器上构建代码时会崩溃
- 错误 LNK1181:在 Visual Studio 2012 中构建代码时无法打开输入文件"Gdi32.lib"
- 这是否会导致内存泄漏/我应该如何构建代码
- 如何在 eclipse 中构建代码之前运行 vcvars32.bat