我的目标是在构建时使用CMake + Clang + GraphViz生成调用图。
使用这些[1,2]进程,我可以创建简单的图形。但是,我不确定如何将流程推广到CMake项目。
我有一个可执行目标。
add_executable(${TARGET} ${SOURCES})
从宏中,我将图形的相关选项添加到目标中:target_compile_options(${TARGET} PRIVATE -S -emit-llvm)
并且,添加一个附加的post build命令来生成调用图:add_custom_command(
TARGET ${TARGET}
POST_BUILD
COMMENT "Running clang OPT"
COMMAND opt -analyze -dot-callgraph
)
但是clang尝试为目标创建可执行文件。这导致此错误:
[build] lld-link: error:
Container.test.cpp.obj: unknown file type
我也不了解任何自定义命令(例如opt
)如何访问生成的LLVM表示形式。我的自定义命令似乎不了解相关文件(即使上述错误已修复)。到目前为止,我的理解是:
add_executable
将-o outfile.exe
参数添加到clang,这使我无法执行链接过程中所示的相同步骤[1,2] $<TARGET_FILE:${TARGET}>
可用于从clang查找生成的文件,但是我不知道这是否适用于LLVM表示。 TARGET
源以及所有设置都放入自定义目标时遇到了问题。 -Wl,-save-temps
特别相关,但这似乎是获取IR的一种round回方式(使用llvm-dis)。 unknown file type
错误是由于对象实际上是LLVM
表示形式引起的,但是我怀疑链接器期望使用其他格式。 LLVM
的表示形式,请将-flto
添加到链接器选项target_link_options(${TARGET} PRIVATE -flto)
(源[4])。这很棒,因为这意味着我几乎解决了这个问题……我只是不知道如何在cmake中获取生成的位码输出文件的路径,一旦这样做,我就可以将它们传递给opt(我希望。 ..)。
$<TARGET_OBJECTS:${TARGET}>
,这将列出.o
(由于cmake重命名,所以.o
吗?)LLVM位代码文件。 .o
文件是位代码,但是opt
工具仅显示为llvm表示形式。转换为此llvm-dis bitcode.bc –o llvm_asm.ll
。由于交叉编译,我认为错误的符号格式很奇怪。将它们传递到llvm-cxxfilt
不会成功,例如llvm-cxxfilt --no-strip-underscore --types ?streamReconstructedExpression@?$BinaryExpr@AEBV?$reverse_iterator@PEBD@std@@AEBV12@@Catch@@EEBAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z
llvm-undname
一起提供,它可以使符号脱嵌。当我运行该工具时,如果给它原始输入,就会出现很大的错误,它似乎只能使用正确的符号。工具demumble
似乎是llvm-undname和llvm-cxxfilt的跨平台,多格式包装器。 11,我几乎可以正常工作的cmake宏如下:
macro (add_clang_callgraph TARGET)
if(CALLGRAPH)
target_compile_options(${TARGET} PRIVATE -emit-llvm)
target_link_options(${TARGET} PRIVATE -flto)
foreach (FILE $<TARGET_OBJECTS:${TARGET}>)
add_custom_command(
TARGET ${TARGET}
POST_BUILD
COMMAND llvm-dis ${FILE}
COMMAND opt -dot-callgraph ${FILE}.ll
COMMAND demumble ${FILE}.ll.callgraph.dot > ${FILE}.dot
)
endforeach()
endif()
endmacro()
但是,这不起作用... ${FILE}
的内容始终是整个列表...这里仍然是这样:
foreach (FILE IN LISTS $<TARGET_OBJECTS:${TARGET}>)
add_custom_command(
TARGET ${TARGET}
POST_BUILD
COMMAND echo ${FILE}
)
endforeach()
结果看起来像:thinga.obj;thingb.obj
这是因为CMake不会在评估for循环之后才评估生成器表达式。意思是,这里只有一个循环,它包含生成器表达式(不是解析的生成器表达式)(源[6])。这意味着我无法遍历目标文件,也无法为每个目标文件创建一系列自定义命令。找到问题后,我将在上面添加内容。如果确定了整个过程,我将发布解决方案。
任何帮助将不胜感激,这是一个巨大的痛苦。
我希望的是一种使CMake接受为单个LLVM表示文件构建可执行文件的方法,该文件与opt一起使用来获取调用图,然后使用
llc
完成编译。但是,在交叉编译时,我受到了一些限制。最终,任何等价的东西都会做...
最佳答案
我将尝试回答一个问题,以收集到目前为止的所有评论答复。
如果您想“颠覆” CMake,可以使用以下方法(从上面OP的第4点中的here改编而成):
cmake_minimum_required(VERSION 3.0.2)
project(hello)
set(CMAKE_C_COMPILER clang)
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello main.c hello.c)
# decide your bitcode generation method here
# target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -c -flto)
# this is just to print
add_custom_target(print_hello_objs
COMMAND ${CMAKE_COMMAND} -E echo $<JOIN:$<TARGET_OBJECTS:hello>," ">)
# this does some linking
# fill in details here as you need them (e.g., name, location, etc.)
add_custom_target(link_hello_objs
COMMAND llvm-link -o foo.bc $<TARGET_OBJECTS:hello>
COMMAND_EXPAND_LISTS)
对于需要对每个文件进行处理的用途,COMMAND
可以是一个外部脚本(bash / python),该脚本仅接收该列表并生成.dot文件。生成器表达式的问题在于,直到生成时间在CMake中且不在foreach
上下文中时,才对它们进行求值。如果要基于重新编译的对象/位代码文件来触发重新生成,由于CMake具有预置的方法来调用工具链的组件(编译器,链接等),因此事情会变得棘手,因此为什么我要编写CMake-based project,但是我强烈建议一开始就避免过度设计,因为听起来好像您不确定自己要面对的是什么。
我还没有为使LTO完全工作而烦恼,因为我在这台机器的ATM上没有这样的设置,因此也可以获得可以工作的可执行文件。
所有其他要求(例如Graphviz输出,分解)可以与其他自定义目标/命令关联。
其他解决方案可能是:
关于c++ - 使用带有CMake和Clang的GraphViz的Callgraphs,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64980827/