c++ - 当 CMake 中的生成器或输入发生更改时,如何仅构建自动生成的代码?

我正在开发一个源代码存储库,该存储库通过运行 python 脚本输出 header 和实现来生成一些 C++ 代码。此代码随后被编译并链接到我的库和可执行文件。我知道只有当两个条件之一为真时,生成的代码才会改变:

  1. 生成器代码本身发生变化
  2. 生成器的输入(XML 文件)发生变化

我想使用 cmake 来管理构建过程。目前,我正在使用 execute_process 来启动生成器。但是,每次我运行 cmake 时都会运行它并且它会触及文件,导致我生成的代码被重新编译并增加了我的总编译时间。

我还想确保生成的代码始终在我的库之前运行。换句话说,我希望库依赖于生成器来运行。

在 cmake 中处理这种情况的正确方法是什么?我已经看到了这个先前的答案:“Get CMake to execute a target in project before building a library”。但这依赖于预先知道的代码生成器的输出。我的代码生成器将生成可变数量的文件。

最佳答案

使用 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/* )

https://stackoverflow.com/questions/26193068/

相关文章:

c++ - 关于如何部署 C++ 代码以在任何地方工作的提示

ios - 使用 iOS 构建主机时 Xamarin 实际在做什么?

build - 递归 CMake 搜索头文件和源文件

c++ - C++ 依赖生产没有自动化的理论原因是什么?

android - 运行 Android Studio gradle build 时如何使用所有 C

gradle - 启动一个 Gradle 守护进程,1 个忙碌和 6 个停止的守护进程无法重用,使用

c - 如何编译 GnuTLS

java - 多个存储库的 Maven 设置

Android NDK - 使两个 native 共享库相互调用

android - 在 Gradle 产品 flavor 上调试签名配置