我应该如何使用CMake设置基于脚本的源代码生成



我正在使用CMake管理某个C++存储库的构建。在这个存储库中,除其他外,我在一个目录中有一堆.hpp文件,对于每一个文件,我都需要编译一个生成的源代码,这取决于它的内容。

简单地说,我会通过生成一个相应的.cpp文件来实现这一点,并将该文件包含在我正在构建的库或可执行文件的CMake目标的源文件中。现在,由于我实际上不需要源文件本身,所以理论上我可以安排编译器从命令行获取源文件。

我的问题是:如何设置这个源代码生成和编译,尽可能地道地使用CMake?

注:

  • 假设我有一个bash脚本,它可以读取.hpp并在标准输出上生成.cpp
  • CMake 3.24版或您喜欢的任何版本
  • 应该在类似Unix的操作系统上工作,希望在类似Windows的操作系统中工作,而不是bash脚本会失败
  • 如果需要补充信息来回答我的问题,请发表评论

为了可移植性,我们假设您有一个Python脚本,而不是管理代码生成的bash剧本。假设它需要两个参数:源.hpp文件和目标.cpp文件。我们假设它在./tools/codegen.py下的源树中。

现在让我们假设您的CCD_ 8文件在CCD_;生成模块";因为这些报头的源是由CCD_ 10生成的。

最后,我们假设有一个最终的可执行目标app,它只有一个源文件./src/main.cpp

下面是一个适用于此的最小构建,并进行了一些逐步讨论。

我们从一些无聊的样板开始。

cmake_minimum_required(VERSION 3.24)
project(example)

这可能适用于早期版本,我只是还没有测试过,所以YMMV。现在,我们将创建可执行目标,并将其预先链接到生成的源。请注意,在调用target_link_libraries之前,不需要存在依赖目标。

add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE genmod)

现在,我们将找到一个Python解释器,并写下代码生成工具的绝对路径。

find_package(Python3 REQUIRED)
set(codegen_py "${CMAKE_CURRENT_SOURCE_DIR}/tools/codegen.py")

接下来,我们将构建输入标头的列表。我想象有三个:A.hppB.hppC.hpp

set(input_headers A.hpp B.hpp C.hpp)
list(TRANSFORM input_headers PREPEND src/genmod/)

我在这里使用了list(TRANSFORM)来保存一些输入。现在我们将创建一个名为genmod的对象库;保持";生成的C++文件的对象。

add_library(genmod OBJECT)

现在真正的肉来了。对于每个标头。。。

foreach (header IN LISTS input_headers)

我们将构造指向标头和生成的源文件的绝对路径。。。

string(REGEX REPLACE "\.hpp$" ".cpp" gensrc "${header}")
set(header "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
set(gensrc "${CMAKE_CURRENT_BINARY_DIR}/${gensrc}")

然后编写一个知道如何调用CCD_ 19的自定义命令。我们指定输出、命令参数和依赖项。不要忘记将生成器脚本作为依赖项包括在内,也不要忘记传递VERBATIM以确保一致的、跨平台的参数引用。

add_custom_command(
OUTPUT "${gensrc}"
COMMAND Python3::Interpreter "${codegen_py}" "${header}" "${gensrc}"
DEPENDS "${header}" "${codegen_py}"
VERBATIM
)

最后,我们将此源附加到genmod

target_sources(genmod PRIVATE "${gensrc}")
endforeach ()

我们可以使用Ninja的试运行功能来测试这个构建,以确保命令看起来正确。

$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Python3: /home/reinking/.venv/default/bin/python3.9 (found version "3.9.2") found components: Interpreter 
-- Configuring done
-- Generating done
-- Build files have been written to: /home/reinking/test/build
$ cmake --build build -- -nv
[1/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/A.hpp /home/reinking/test/build/src/genmod/A.cpp
[2/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/B.hpp /home/reinking/test/build/src/genmod/B.cpp
[3/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/C.hpp /home/reinking/test/build/src/genmod/C.cpp
[4/8] /usr/bin/c++   -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/A.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/A.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/A.cpp.o -c /home/reinking/test/build/src/genmod/A.cpp
[5/8] /usr/bin/c++   -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/B.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/B.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/B.cpp.o -c /home/reinking/test/build/src/genmod/B.cpp
[6/8] /usr/bin/c++   -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/C.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/C.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/C.cpp.o -c /home/reinking/test/build/src/genmod/C.cpp
[7/8] /usr/bin/c++   -O3 -DNDEBUG -MD -MT CMakeFiles/app.dir/src/main.cpp.o -MF CMakeFiles/app.dir/src/main.cpp.o.d -o CMakeFiles/app.dir/src/main.cpp.o -c /home/reinking/test/src/main.cpp
[8/8] : && /usr/bin/c++ -O3 -DNDEBUG  CMakeFiles/genmod.dir/src/genmod/A.cpp.o CMakeFiles/genmod.dir/src/genmod/B.cpp.o CMakeFiles/genmod.dir/src/genmod/C.cpp.o CMakeFiles/app.dir/src/main.cpp.o -o app   && :

事实上,我们可以看到这些命令是我们自然期望的

最新更新