预处理之后和使用CMake编译之前的自定义构建步骤

Custom build step after preprocessing and before compilation with CMake

本文关键字:自定义 构建 编译 之后 CMake 预处理      更新时间:2023-10-16

我正试图在预处理和使用CMake编译之间添加一个自定义构建步骤。

在c++预处理器步骤之后,我想在每个预处理的源文件上调用一个python脚本来修改它们。

这里有一个Makefile的例子:

all : prog
# compilation step
prog: main.i
gcc main.i -g -Wall -o prog 
# custom step
main.i: main.tmp
./my_script.py main.tmp > main.i
# only preprocessor step
main.tmp: main.c main.h
gcc -E main.c > main.tmp

CMake如何实现这一点?该步骤应应用于项目的每个ccpp文件。

使用add_custom_command的第一个签名来运行预处理文件的命令,然后运行执行自定义附加处理的命令,并将OUTPUT定义为自定义处理创建的文件,然后在定义该文件时(通过add_library/add_executable)将其列为相关目标的源文件,或者在定义后(通过target_sources)执行该操作。

如果你想采用我将展示的方法,那么在CMake中使用编译器标志和定义将成为一件非常棘手的事情。通常,您需要确保预处理步骤命令与编译步骤命令具有完全相同的编译器定义和标志。如果预处理和编译之间的标志或定义不同,可能会发生非常奇怪、微妙甚至可能危险的事情。你可能会遇到编译器错误,链接器错误,运行时错误——所有这些都可能很难理解——或者你可能没有遇到任何错误,但仍然犯了一个错误。您需要非常小心,了解编译器并检查预处理器输出,以确保您做得正确。

可能有更好的方法,但我没有想过。我从来没有在真正的项目中这样做过。下面的答案只是理论。把它当作一个起点。它不提供任何保修(<在此处插入您在开源许可证中经常看到的法律保修声明>)。虽然我会努力纠正发现的错误,并保持最新信息,但如果使用此信息导致任何问题,我不会承担责任。

Ex。对于gcc(注意:有关-E.ii的文档,请参阅此处):

list(APPEND my_target_sources "${CMAKE_CURRENT_SOURCE_DIR}/file.cpp")
# this is best placed immediately after the creation of the target so that it knows all the directory compile options and such that the target saw when it got created.
foreach(input_file ${my_target_sources})
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/src-$<CONFIG>/file.ii")
get_target_property(target_compile_opts my_target COMPILE_OPTIONS)
add_custom_command(
OUTPUT "${output_file}"
COMMAND "${CMAKE_CXX_COMPILER}" ${CMAKE_CXX_FLAGS}  ${target_compile_opts} -o "${output_file}" -E "${input_file}"
COMMAND my_additional_custom_processing_script "${output_file}" my_arg1 my_arg2 my_arg3
DEPENDS "${input_file}" "my_additional_custom_processing_script"
VERBATIM COMMAND_EXPAND_LISTS # these two args are generally useful, but not actually required here.
)
target_sources(my_target PRIVATE "${output_file}")
# this might be needed since I don't expect cmake to infer CXX from the .ii extension
set_property(SOURCE "${output_file}" TARGET_DIRECTORY my_target PROPERTY LANGUAGE CXX)
endforeach()

当询问者使用python脚本时,为了对其他未来的读者具有通用性,并试图将其他非常相似的问题标记为重复问题,我尝试将该部分保留一点通用性,并且将其编写为主机系统可以在PATH上找到该命令,并且可以自行运行(即,如果脚本假设它有一个适当的shebang)。修改自定义预处理命令部分以适合您的用例。

如果要在每个源文件的基础上设置任何编译器选项,则需要确保在该文件的自定义命令中设置这些选项,并在经过后预处理的源文件的COMPILE_OPTIONS前缀中设置它们。

如果预处理输出依赖于特定于配置的通用编译器标志(CMAKE_<LANG>_FLAGS_<CONFIG>),则需要将右生成器表达式中封装的变量添加到COMMAND,例如"$<$<CONFIG:Debug>:${CMAKE_CXX_FLAGS_DEBUG}>"。我不知道有什么更漂亮的方法可以为所有配置做到这一点。

当前缺少此答案

  • 预处理时从目标的依赖项设置INTERFACE编译器选项
  • 将与目标相关联的编译定义传递给预处理器步骤命令。
    • 在我或其他人找到一个好方法之前,当涉及到NDEBUG宏的存在或不存在时,您可能会立即注意到这一点

我真的希望有人知道更好的方法,因为我的答案已经成熟,有各种各样的错误机会。

事实上,这必须手动完成:我添加了一个自定义目标,在那里我生成预处理文件:

add_custom_target(
start_preprocessor 
COMMAND make main.cpp.i 
..
)

使用另一个自定义目标,我备份main.cpp,然后将main.cpp.I重命名为main.cpp。然后我可以应用我的预处理过程。之后,我恢复了源文件。