我认为你最终想要的是能够构建一个 C 程序
使用 CMake 和 clang 的项目,其中源文件被编译为 LLVM 位码
可执行文件是从位码文件链接的。
使用 CMake,要求 clang 链接位码文件意味着要求它链接LTO mode https://llvm.org/docs/LinkTimeOptimization.html,
与-flto
联动选项。
你可以得到叮当声compile到 LLVM 位码-flto
汇编
选项,或与-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 ..
Build:
$ 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
你将找不到任何*.bc
Makefile 中的目标,也没有任何*.bc
文件
生成:
$ egrep -r '.*\.bc'; echo Done
Done
$ find -name '*.bc'; echo Done
Done
因为编译选项-flto
or -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!
Later
当我尝试“ 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
你可以在那里看到.o
由以下文件组成的文件hello.c
and main.c
由 CMake 生成的 makefile 不会被调用hello.o
and main.o
but hello.c.o
and main.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
as a .PHONY target https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html这取决于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