2016-06-08 36 views
5

我們有一個代碼生成器,可以將通信協議定義生成多個C++文件,並且希望將我們的生成系統切換爲使用cmake。 我做了一個示例項目來演示我們面臨的問題。cmake動態生成的源文件列表

碼生成器接收輸入文件,並且可以產生幾百 輸出文件,以便列舉它們CMakeLists.txt沒有特別的,因爲在開發過程中的協議改變的選項。我們既不想爲此使用通配符。

根據文檔我們認爲configure_file應該能夠做到這一點,但它顯然在它的工作上失敗了,我們沒有找到一種方法讓cmake重新生成makefile文件,如果其中一個輸入文件發生更改。

我們希望爲主營項目的生成文件(在例子中main文件夾),以通過cmake的時候要麼發電機再生(在例子中gen文件夾)的代碼更改或協議定義文件(在我們的例子中是in.txt)已更新。

我有以下文件結構:

src 
├── CMakeLists.txt 
├── gen 
│   ├── CMakeLists.txt 
│   └── gen.cpp 
├── in.txt 
└── main 
    ├── CMakeLists.txt 
    └── main.cpp 

具有以下內容: SRC /的CMakeLists.txt:

cmake_minimum_required(VERSION 3.5.1) 
project(cmake-config) 

add_subdirectory(main) 
add_subdirectory(gen) 

add_dependencies(main gen run_gen) 

GEN /的CMakeLists.txt:

cmake_minimum_required(VERSION 3.5.1) 
project(gen) 

add_executable(gen gen.cpp) 

add_custom_target(
    run_gen 
    COMMAND ./gen ${CMAKE_SOURCE_DIR}/in.txt 
    COMMENT running gen 
) 

add_dependencies(run_gen gen) 

gen/gen.cpp:

#include <fstream> 
#include <string> 

using namespace std; 

int main(int argc, char *argv[]){ 
    ifstream inf(argv[1]); 
    ofstream outf("input.txt"); 
    string word; 
    while(inf >> word) 
     outf << "set(SOURCES ${SOURCES} " << word << ")\n"; 

} 

主/的CMakeLists.txt:

cmake_minimum_required(VERSION 3.5.1) 
project(main) 

if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/../gen/input.txt) 
    file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/../gen/input.txt "") 
endif() 

configure_file(${CMAKE_CURRENT_BINARY_DIR}/../gen/input.txt ${CMAKE_CURRENT_BINARY_DIR}/input.txt COPYONLY) 

set(SOURCES main.cpp) 

include(${CMAKE_CURRENT_BINARY_DIR}/input.txt) 

message(${SOURCES}) 

add_executable(main ${SOURCES}) 

主/ main.cpp中:

int main(){} 

gen生成input.txt使用src/in.txtinput.txt包含我想用它來構建main源文件的列表:

set(SOURCES ${SOURCES} a.cpp) 
set(SOURCES ${SOURCES} b.cpp) 
set(SOURCES ${SOURCES} c.cpp) 

我們正在尋找不需要引導cmake溶液或需要發展的過程中,每次重新運行該協議的變化。這很容易實現,但不受歡迎,因爲如果團隊中的某些人沒有意識到協議文件(或生成器)發生了變化,則會導致產生有問題的代碼,最終會使用較舊的協議定義。

回答

0

我們找到了一種方法。生成器gen放在一個單獨的項目中,而不更改文件結構。 gen是在解析頂級CMakeLists.txt文件時生成並執行的。當頂級make被調用時,它修改CMakeLists.txt,因此下一次make被稱爲cmake自動運行。項目gen有一個自定義命令,在in.txt更改時重新生成輸出。

的src /的CMakeLists.txt:

cmake_minimum_required(VERSION 3.5.1) 
project(cmake-config) 

if(NOT EXISTS ${CMAKE_BINARY_DIR}/gen/Makefile) 
    execute_process(COMMAND cmake -B${CMAKE_BINARY_DIR}/gen -H${CMAKE_SOURCE_DIR}/gen) 
endif() 
execute_process(COMMAND make -C ${CMAKE_BINARY_DIR}/gen) 

add_custom_target(touch_cmakelists.txt ALL 
    COMMAND touch ${CMAKE_SOURCE_DIR}/CMakeLists.txt 
) 

add_subdirectory(main) 

GEN /的CMakeLists.txt:

cmake_minimum_required(VERSION 3.5) 
project(gen) 

add_executable(gen gen.cpp) 

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt 
    COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gen ${CMAKE_SOURCE_DIR}/../in.txt 
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gen ${CMAKE_SOURCE_DIR}/../in.txt 
    COMMENT "Running gen" 
) 

add_custom_target(run_generator ALL 
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/input.txt 
) 

主/的CMakeLists.txt:

project(main) 

set(SOURCES main.cpp) 

include(${CMAKE_CURRENT_BINARY_DIR}/../gen/input.txt) 

add_executable(main ${SOURCES}) 
+0

這似乎基本上解決了[這裏]相同的問題(http://stackoverflow.com/q/36084785/1938798)。您可能會發現這裏的討論更加詳細。它採用了類似的方法,爲生成器設置子構建,以便爲主構建創建文件列表。 –

0

我認爲你可以完全自動化這個過程。 您需要一個可以處理文件參數列表的宏。 下面是一個例子,我用它來處理Zeroc Ice中間件的接口文件,該中間件成爲項目其餘部分的源文件。 您還缺少的是「ADD_CUSTOM_COMMAND」的「MAIN_DEPENDENCY」參數。

macro(sliceCpp outfiles) 
    foreach(i ${ARGN}) 
     GET_FILENAME_COMPONENT(name ${i} NAME) 
     GET_FILENAME_COMPONENT(name2 ${i} NAME_WE) 
     SET(infile ${CMAKE_CURRENT_SOURCE_DIR}/${name}) 
     SET(out1 ${CMAKE_CURRENT_BINARY_DIR}/${name2}.cpp) 
     SET(out2 ${CMAKE_CURRENT_BINARY_DIR}/${name2}.h) 
     ADD_CUSTOM_COMMAND(
      OUTPUT ${out1} ${out2} 
      COMMAND ${ICE_SLICE2CPP} 
      ARGS -I${ICE_SLICE_DIR} -I${CMAKE_CURRENT_SOURCE_DIR} --output-dir ${CMAKE_CURRENT_BINARY_DIR} ${infile} 
      MAIN_DEPENDENCY ${infile} 
     ) 
     SET(${outfiles} ${${outfiles}} ${out1} ${out2}) 
    endforeach(i) 
    INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) 
endmacro(sliceCpp) 

因此,CMake會理解依賴關係,只重新構建必要的東西。生成用於接口的cpp文件的其他框架的行爲可能類似,您可以看看ROS generate_messages()https://github.com/ros/ros_comm/blob/kinetic-devel/clients/roscpp/rosbuild/roscpp.cmake

+0

我們的問題是,我們沒有文件名稱列表執行'cmake'時。該列表由主項目的自定義目標生成,並放入input.txt。最初這個文件是空的,因此我們想觸發'cmake'重新讀取它。 – robert