2014-10-04 52 views
16

我正在通过运行输出标头和实现的python脚本来生成一些C++代码的源代码库。此代码随后被编译并链接到我的库和可执行文件。我知道,如果两个条件之一为真生成的代码才会改变:如何在CMake中生成器或输入发生更改时才生成自动生成的代码?

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

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

我也想确保生成的代码之前我总是库运行。换句话说,我希望库依靠发生器来运行。

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

+1

'导致我生成的代码被recompiled'那么可能是你需要提高你的发电机? '发电机代码本身改变''configure_file可以做到这一点:http://stackoverflow.com/a/26076132/2288008 – 2014-10-05 18:26:52

+0

@ruslo:只是为了检查我是否理解你的权利......我将让代码生成器吐出它以CMAKE可以读取的格式生成的文件列表? (一个SET(foo1 foo2)列表)... – 2014-10-05 23:17:11

+0

@ruslo:另一件事。我知道生成的文件在哪里结束,并将它们包括在最终的可执行文件中,目前是使用文件(GLOB)完成的。我可以继续这么做吗? – 2014-10-05 23:21:19

回答

22

使用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_DEPENDENCY(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/*) 
+1

只是偶然发现这一个http://stackoverflow.com/questions/24416133/cmake-add-custom-command-issue-with-multiple-output-files(没有尝试重现这一点),你可能需要/需要在尝试“ADD_CUSTOM_COMMAND”时查看。 – 2014-10-06 07:52:09

+0

我试过自定义命令。不幸的是,我不知道我将生成哪些文件(所以OUTPUT是未知的)。有没有办法解决这个问题? – 2014-10-06 11:27:31

+0

其实,我可以修改我的代码生成器,始终输出一个“keystone”文件,该文件可用于跟踪上次运行的时间。之后,我可以为实际的OUTPUT文件(GLOB)...或者每次运行GLOB并导致完全重建? – 2014-10-06 11:32:10

0

我在工作时遇到过类似的问题。只有我们不使用cmake而是使用另一个构建系统。

我不知道cmake的,但是这可能会给你一些想法:

我们的解决方案涉及处理XML,并在发电机作为代码需要编写,治疗产生的依赖性和XML文件作为资源。如果生成器或xml文件发生变化,构建系统会运行生成器(生成所有内容 - 即使只更改了一个文件,这意味着它会触及所有生成的文件)。

构建系统当然没有直接运行生成器,而是我们编写了一个小的python脚本,它决定了如何正确运行生成器 - 例如,我们可以添加的一个改进是将所有文件生成到/ tmp,移动已更改的文件(使用diff进行比较),因此只会更改已更改的文件。 (我们并不需要它的时刻为我们的文件不经常变化)

我们也结束了两个不同的图形,一个图形发电机和其他图形的其他文件运行构建系统的两倍。我们将其设计为允许多个级别的生成器生成的依赖关系,以便一个生成器可以依赖另一个生成器生成的产品。

另外两个技巧来考虑,如果你的编译系统允许您使用正则表达式来构建文件,你可能会想使用它。另外,您可以在生成过程中为您的构建系统生成配置文件。

+0

感谢您的输入。我想知道cmake是否有更简单的方法。 – 2014-10-04 17:19:43

1

低级解决方案在另一个目录中生成文件,使文件与当前文件进行比较,如果它们不同,则不进行复制,不进行重新编译。

这当然只在发生器没有填充一些像版本和日期那样的随机噪声时才起作用。在这种情况下,您可以尝试将其过滤掉。

1

今天我有或多或少的这个问题。我将二进制资源嵌入到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() 
+0

Intersting方法! – 2016-04-02 10:12:07