2014-10-04 50 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