我们有一个代码生成器,可以将通信协议定义生成为多个c ++文件,并希望切换我们的构建系统以使用cmake。 我已经制作了一个示例项目来展示我们面临的问题。
代码生成器接受一个输入文件,可能会生成数百个
输出文件,因此在CMakeLists.txt
中枚举它们不是一个选项,特别是因为协议在开发过程中发生了变化。我们既不想使用通配符。
我们没有找到让cmake重新生成makefile的方法,如果其中一个输入文件发生了变化,根据文档,我们认为configure_file
应该能够做到这一点,但显然失败了。
我们希望每当生成器(在示例中的main
文件夹中)代码更改或协议定义文件(cmake)时,都会重新生成主项目的makefile(在示例中的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.txt
。 input.txt
包含我要用于构建main
的源文件列表:
set(SOURCES ${SOURCES} a.cpp)
set(SOURCES ${SOURCES} b.cpp)
set(SOURCES ${SOURCES} c.cpp)
我们正在寻找一种解决方案,它不需要引导cmake
,也不需要在每次协议在开发过程中更改时重新运行它。这很容易实现,但是不可取,因为如果团队中的某个人没有意识到协议文件(或生成器)已经发生变化,那么最终会使用较旧的协议定义导致问题代码。
答案 0 :(得分:0)
我认为您可以完全自动化该过程。 您需要一个可以处理文件参数列表的宏。 这是一个用于处理Zeroc Ice中间件的接口文件的示例,该中间件成为项目其余部分的源文件。 您还缺少的是&#34; MAIN_DEPENDENCY&#34;参数&#34; ADD_CUSTOM_COMMAND&#34;。
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()
here
答案 1 :(得分:0)
我们找到了一种方法。生成器gen
放在一个单独的项目中,而不更改文件结构。在解析顶级CMakeLists.txt文件时构建并执行gen
。当调用顶级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})