2016-06-08 93 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