makefile相关(+elf)
最近需要阅读vim的源代码,但是好好的一个.c文件里满屏的#ifdef、#endif…….让我怀疑自己到底有没有学过C语言,百度了一下,有建议先学一下makefile,粗略了解一下。
makefile
好处是自动化编译
make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。
makefile里有什么?
- 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
- 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
- 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
- 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。
- 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“/#”。
makefile的书写规则:
? 1.如果这个工程没有编译过,那么所有c文件都要编译并被链接
? 2.如果这个工程的某几个c文件被修改,那么只需要编译被修改的c文件,并链接目标程序
? 3.如果这个工程的头文件被修改,那么需要编译引用了这几个头文件的c文件,并链接目标程序
makefile可以使我们只用一个make命令,就可以使其根据当前的文件修改情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。
make是如何工作的?(只输入make命令)
? 1.make会在当前目录下找名字叫“Makefile”或“makefile”的文件
? 2.如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件
? 3.如果目标文件不存在,或是目标文件所依赖的后面的 .o文件的文件修改时间要比当前这个文件新,那么,他就会执行后面所定义的命令来生成当前这个目标文件
? 4.如果目标所依赖的.o文件也存在,那么make会在当前文件中找目标 为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
? 5.当然,C文件和头文件是存在的,于是make会生成 .o 文件, 然后再用 .o 文件生成make的终极任务,也就是执行目标文件了
? 以上就是整个make的依赖性,make会一层又一层地去找文件的依赖关系, 直到最终编译出第一个目标文件。如果没找到依赖文件,make就会报错退出。
如vim1.14中的规则:
amiga.o: amiga.c $(INCL) amiga.h cc $(CFLAGS) -hi$(INCL) amiga.c
amiga.o是目标,amiga.c和amiga.h是目标所依赖的源文件。如果amiga.c和amiga.h的文件日期比amiga.o文件日期要新,或是amiga.o不存在,那么依赖关系发生。
如果生成(或更新)amiga.o文件,也就是cc命令(C语言编译程序默认命令,CFLAGS为C语言编译器参数)说明了如何生成/更新
GNU的make工作时的执行步骤:
- 读入所有的makefile
- 读入被include的其它makefile
- 初始化文件中的变量
- 推导隐晦规则,并分析所有规则
- 为所有的目标文件创建依赖关系链
- 根据依赖关系,决定哪些目标要重新生成
- 执行生成命令
thanks to:
https://blog.csdn.net/haoel/article/details/2886
https://blog.csdn.net/weixin_38391755/article/details/80380786
条件编译
可以看出vim1.14的源程序中有许多#ifdef、#endif字段,这属于条件编译。源代码中的所有行都参加编译,但有时希望对其中一部分内容在其满足一定条件时才编译。
e.g. #ifdef AMIGA(标识符)
void(程序段1)
#else
int(程序段2)
#endif
即当标识符已经被定义过,则对程序段1进行编译,否则编译程序段2
#ifndef==if not defined:最主要的目的是防止头文件的重复包含和编译。检查预编译常量在前面是否已经被宏定义,如果没有被宏定义,则条件指示符#ifndef为真。
比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
e.g. #ifndef x //如果没有定义x #define x //定义x #endif //结束如果
#undef取消前面由#define定义的预处理器标识符
read Vim1.14
amiga.c:
ArpBase
IsInteractive()//判断是否为交互窗口
quickfix mode:Quickfix模式的主要思想是保存一个位置列表,然后提供一系列命令,实现在这个位置列表中跳转。在vim启动编译,然后vim会根据编译器输出的错误信息,自动跳到第一个出错的地方,让你进行修改;修改完后,使用一个快捷键,跳到下一个错误处,再进行修改,方便的很。
ELF(executable and linkable format)
一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。
ELF文件由4部分组成:ELF header、程序头表、节、节头表
只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。
ELF文件格式主要有三种:
1)可重定向文件:文件保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者是一个共享目标文件。(目标文件或者静态库文件,即linux通常后缀为.a和.o的文件)
2)可执行文件:文件保存着一个用来执行的程序。(例如bash,gcc等)
3)共享目标文件:共享库。文件保存着代码和合适的数据,用来被下连接编辑器和动态链接器链接。(linux下后缀为.so的文件。)
ELF header
在文件的开始,保存了路线图,描述了该文件的组织情况。(/usr/include/elf.h 可查看ELF的文件头)