现在我们将创建一个程序,该程序能够读取次位码文件并打印其中定义的函数名称,以及它们的基本块数,从而显示LLVM库的易用性
什么是Makefile?
- C语言中,我们使用visual studio开发软件时候,写程序开始时候都会创建一个project项目文件,然后在文件里面编译 .h 和.c 的文件。
- 在Linux中,有一个叫make的东西,就相当于C语言的集成开发环境,我们只需要在make里面创建文件,写代码,make会帮我们管理这些文件。
- 不过我们创建的项目不叫project,而是称为Makefile,打开一个make源程序包,发现很多Makefile的文件,说明里面有很多的项目。
- 在源程序包里面,也有名为makefile的文件(m是小写),两个命名同时存在,这是合理的,在开发一个项目的时候,工程师一般都会命名为Makefile然后打包交给用户,用户觉得某个Makefile需要改动,用户改动后或者新建后的项目定义为makefile,并且在运行时候,先执行makefile,再执行Makefile文件。
Makefile文件
链接LLVM库需要使用长命令行,如果没有构建系统的帮助,想写出这些命令行是不切实际的
注意:Makefile依赖于制表符来指定定义规则的命令
即应该手动插入制表符(下面有解释)
这个Makefile文件基于(DragonEgg中使用的代码)
解释一:
第一部分定义将用作编译器标志的第一个Makefile变量
第一个变量决定llvm-config程序的位置
llvm-config工具是一个LLVM程序
它可以构建需要与LLVM库连接的外部项目的各种有用信息
Ag:
定义在C++编辑器中使用的标志集时
我们将要求Make启动llvm-config --cxxflags
shell命令行,该命令行将打印用于编译LLVM项目的C++标志集
这样就使得项目源码的编译与LLVM源码兼容。
最后一个变量定义要传递给编译器预处理的标志集
解释二:
第二个片段
定义了Makefile的规则
第一个是默认的,用它构建hello-word可执行文件
第二个是通用规则,将所有c++文件编译生成目标文件
将预处理标志和C++编辑器标志传递给它
用$(QUIET)变量来省略屏幕上出现的完整命令
如果想要一个详细的构建日志,运行GUN Make 时定义VERBOSE
最后一个链接规则所有目标文件(在这里只有一个)来构建与LLVM库链接的项目可执行文件
这部分由链接器完成的,但是一些C++标志也可能会生效。
因此我们将C++和链接器标志都传递给命令行
用“command”的结构来完成此操作,它指示shell“command”的输出替换这部分内容
在Ag中命令是 llvm-config --libs bitreader core support
“–libs 标志向llvm-config请求提供链接到所有LLVM库中的链接器标志列表
请求libLLVMBItReader、libLLVMCore、libLLVMSupport
由llvm-config返回的标志列表是一系列-l链接器参数
Ag:-lLLVMCore-lLLVMSupport
注意
传递给链接器的参数顺序很重要,并且要求你依赖于其他库的参数放在前面
Ag:
由于libLLVMCore使用libLLVMSupport提供的通用功能,因此正确顺序是-lLLVMCore-lLLVMSupport
顺序很重要,因为一个库就是一个目标文件的集合,将在项目与库链接时
链接器只选择到目前为止目标文件来解析见到的未定义的符号
因此,如果它正在处理命令行参数的最后一个库,并且该库恰好使用已经处理的库中的符号
则大多数链接器(包括GUN id)将不会返回去包括有可能确实的目标文件,从而导致构建失败
如何避免这个问题?
强制链接器迭代访问每个库,直到所有必要的目标文件都被解析,则必须在库列表的开始和结束出使用–start–group和–end-group标志,但有可能会减慢速度
在构建完整的依赖关系图时,为了避免因为要弄清楚链接器参数的顺序而头疼
可以简单的使用llvm-config --libs 让它为你这些工作
解释三
Makefile文件的最后一部分定义了一条清理规则以删除编译器生成的所有文件
使我们从头开始从新启动构建
长命令行
在计算机科学中,长命令行是指包含大量参数和选项的命令行。这些参数和选项用于控制命令的行为。
长命令行通常有以下特点:
- 包含多个参数和选项
- 参数和选项之间使用空格或分隔符隔开
- 命令行的总长度可能超过一行
长命令行的示例:
# 这是一个长命令行示例gcc -o hello hello.c -Wall -Wextra -pedantic -std=c11 -g -O2
在这个示例中,gcc
是命令,-o
、-Wall
、-Wextra
、-pedantic
、-std=c11
、-g
和 -O2
是参数和选项。
长命令行有以下缺点:
- 难以阅读和理解
- 容易出错
- 不易于维护
为了解决这些问题,可以使用构建系统来帮助管理长命令行。构建系统可以自动生成命令行,并根据需要进行调整。
以下是一些常见的构建系统:
- CMake
- Makefile
- Autotools
- Bazel
使用构建系统可以带来以下好处:
- 提高命令行的可读性和可理解性
- 减少错误的可能性
- 提高代码的可维护性
因此,如果需要使用长命令行,建议使用构建系统来帮助管理。
回到您最初的问题:“链接LLVM库需要使用长命令行,如果没有构建系统的帮助,想写出这些命令行是不切实际的。”
这句话的意思是,链接 LLVM 库通常需要使用大量的参数和选项。这些参数和选项用于指定要链接的库、编译器选项等信息。如果没有构建系统的帮助,手动编写这些命令行非常繁琐,而且容易出错。
使用构建系统可以自动生成链接 LLVM 库所需的命令行。这可以大大提高效率,并减少错误的可能性。
以下是一些使用构建系统链接 LLVM 库的示例:
- CMake
cmake_minimum_required(VERSION 3.10)project(Hello)set(CMAKE_CXX_STANDARD 11)add_executable(hello hello.c)target_link_libraries(hello PRIVATE LLVM)
- Makefile
all:gcc -o hello hello.c -lLLVMclean:rm -f hello
在这个示例中,-lLLVM
选项用于链接 LLVM 库。
制表符
在 Makefile 中,制表符(Tab)是一种特殊的缩进符,用于指定定义规则的命令。它与空格不同,不能被空格替换。
具体来说,制表符在 Makefile 中有以下作用:
- 区分命令和目标:Makefile 中的每一行都由目标和命令组成。目标是 Makefile 要生成的文件或要执行的命令,而命令是用于生成目标的具体操作。制表符用于将目标和命令区分开来。目标的后面必须跟一个冒号(:),然后是命令。命令必须以制表符开头。
- 指定命令的缩进:Makefile 中的命令可以有多行,但只有第一行需要以制表符开头。后续行的缩进可以是制表符或空格,但必须一致。
- 控制命令的执行顺序:Makefile 中的命令是按照顺序执行的。如果命令有多行,则第一行的制表符决定了该命令的执行顺序。制表符的数量越多,命令的执行顺序越靠后。
以下是一个简单的 Makefile 示例:
all:echo "Hello, world!"clean:rm -f hello.txt
在这个示例中,all
是目标,echo "Hello, world!"
是命令。命令以制表符开头,因此它将在 all
目标之后执行。
clean
是另一个目标,rm -f hello.txt
是命令。命令以制表符开头,因此它将在 clean
目标之后执行。
需要注意的是,Makefile 中的制表符和空格是严格区分的。如果使用空格代替制表符,则 Makefile 可能无法正确执行。
以下是一些使用制表符的注意事项:
- 制表符的宽度:不同的编辑器可能会对制表符的宽度进行不同的设置。为了确保 Makefile 在不同的编辑器中都能正确执行,建议将制表符的宽度设置为 8 个空格。
- 混合使用制表符和空格:在 Makefile 中,最好不要混合使用制表符和空格。如果需要使用空格进行缩进,则建议在所有行都使用空格,而不是只在某些行使用空格。