CMakeList文件可从C源文件生成

时间:2019-02-25 11:12:50

标签: linux makefile cmake llvm llvm-clang

我正在尝试使用CMake从C源文件(hello.c)生成LLVM字节码文件。下面是我的CMakeLists文件。

###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-emit-llvm")

project (hello)
add_executable(hello hello.c)
  1. 我是CMake的新手,不确定这是否正确。我在生成的MakeFile中找不到任何使* .bc的规则 。请在这里纠正我。我也尝试过“ -save-temps”

  2. 将其视为单个.c文件。如果您能给我一些有关为完整的C项目生成相同内容的提示,那将真的很有帮助。

3 个答案:

答案 0 :(得分:1)

完成后,

$>file CMakeFiles/hello.dir/hello.c.o
CMakeFiles/hello.dir/hello.c.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

如果     设置(CMAKE_C_FLAGS“ -emit-llvm”)

之前写过

project (hello)

为了获得IR位码,我写道:

###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
project (hello)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-flto")
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello hello.c)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)

我花了几个小时才能使Makefile从IR编译 使用lld将代码编码为本地代码,然后使用cmake则要快得多。 然后在cmake生成的Makefile中阅读,我能够更正我的Makefile:

clang  -flto  -flto <hello.c.o> ..

这行得通,但我不知道为什么-flto被写两次。

非常感谢您发表这篇文章,将clang展示为llvm提供的各种命令的集中前端。

答案 1 :(得分:0)

我认为您最终想要的是能够构建C程序 CMake和clang的项目,其中源文件被编译为LLVM位码 并且可执行文件是从位码文件链接的。

对于CMake,要求clang链接位代码文件意味着要求其链接LTO mode中的内容, 使用-flto链接选项。

您可以使用-flto编译使clang编译为 到LLVM位代码 选项,或使用-emit-llvm选项。

为说明起见,这里是一个Hello World项目,其中包含两个源文件和一个标头:

$ ls -R
.:
CMakeLists.txt  hello.c  hello.h  main.c

这是:

CMakeLists.txt

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)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)

它在以下情况下也能很好地工作:

#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)

为CMake创建构建目录,然后转到那里:

$ mkdir build
$ cd build

生成构建系统:

$ cmake ..

内部版本:

$ make
Scanning dependencies of target hello
[ 33%] Building C object CMakeFiles/hello.dir/main.c.o
[ 66%] Building C object CMakeFiles/hello.dir/hello.c.o
[100%] Linking C executable hello
[100%] Built target hello

在Makefile文件中找不到任何*.bc个目标,也没有*.bc个文件 生成:

$ egrep -r '.*\.bc'; echo Done
Done
$ find -name '*.bc'; echo Done
Done

因为编译选项-flto-emit-llvm导致输出 文件:

CMakeFiles/hello.dir/main.c.o
CMakeFiles/hello.dir/hello.c.o

符合通常的CMake命名约定,但实际上不是目标文件 但是您看到的是LLVM位代码文件:

$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o:  LLVM IR bitcode

程序执行常规操作:

$ ./hello 
Hello World!

稍后

  

当我尝试“ make hello.o”时,它应该生成目标文件吗?   cmd成功执行,但是找不到生成的目标文件。我做对了吗?

您正在以一种正确的方式进行操作,尽管这不是唯一正确的方式,但是 您的期望是错误的。再看看:

$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o:  LLVM IR bitcode

您可以在那里看到由.ohello.c组成的main.c文件 CMake生成的makefile生成的hello.o不是main.o,而是hello.c.omain.c.o。 CMake更喜欢使用编译后的文件名来保留扩展名。 源文件,并附加.o。这是相当普遍的做法。所以如果你想 使用makefile来编译hello.c,最明显的正确方法是 make hello.c.o

让我们看看实际发生了什么。在我的CMake构建目录中:

$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
make[1]: 'CMakeFiles/hello.dir/hello.c.o' is up to date.
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

无事可做,因为我的hello.c.o是最新的。所以我会 删除并重复:

$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang   -flto -o CMakeFiles/hello.dir/hello.c.o   -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

现在,它已经重新编译。

但是,由于许多人(例如您)希望hello.o被编译 来自hello.c的CMake有助于将hello.o定义为.PHONY target 取决于hello.c.o

$ egrep  -A3 'hello.o.*:.*hello.c.o' Makefile 
hello.o: hello.c.o

.PHONY : hello.o

实际上,我可以这样做:

$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang   -flto -o CMakeFiles/hello.dir/hello.c.o   -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

make hello.o是制作hello.c.o

的另一种方法

答案 2 :(得分:0)

问题在于,使用-emit-llvm标志不会产生最终的二进制文件,并且一旦在其中使用了该标志,CMake将停止配置测试。

除了关于使用LTO基础结构的文章外,还有3种(或2种半)其他选择。

一种方法是使用Whole-Program LLVM并使用提供的命令来提取相关的位码部分。

另一种方法是采用手动方式在CMake二进制目标上设置自定义目标(请参见add_custom_targetadd_custom_command),该目标将在更改时触发,并且将再现所需的结果,就像每次在命令行上手动执行。

现在,在最后一点上,我有类似的需求,因此我创建了一个提供该功能(llvm-ir-cmake-utils)的CMake项目,但允许您随意将这些自定义目标连接到现有目标上无需每次都从头开始重写所有内容。

回购中有一些示例,但总之,它允许您将自定义目标附加到已经存在的CMake目标上,例如

[...]
add_executable(qux ${SOURCES})

[...]
# this will create a bitcode generating target 
# and allow it to depend on the initial target in order to detect source code changes
llvmir_attach_bc_target(qux_bc qux)
add_dependencies(qux_bc qux)
[...]