一份简单的Makefile介绍入门,帮助在Linux系统下编译C/C++。
引出
如果有一个test.cpp
文件,在命令行里输入
1 | g++ test.cpp -o test |
即可对cpp文件进行编译,生成test
可执行文件。
如此,每次对cpp文件进行修改后,都要重新输入该命令进行编译,过程复杂且重复。
所以,可以将这些命令打包成makefile文件,定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
在该文件夹下生成一个空文件,命名为Makeflie
或makefile
1 | test: test.cpp |
第二行的开头为Tab键,如为空格键则会出现makefile:2: *** missing separator. Stop.
的报错。
VSCode中默认Tab键转化为空格,如需输入Tab键,关闭设置中Editor: Insert Spaces选项。
在命令行中输入
1 | make |
即可对test.cpp
文件进行编译,效果与gcc test.cpp -o test
相同。
Makefile的规则
Makefile的基础框架
1 | target: prerequisites |
- target(目标):可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)
- prerequisites(依赖):生成该target所依赖的文件和target
- command(命令):该target要执行的命令(任意的shell命令)
target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。
一个示例
1 | edit : main.o kbd.o command.o display.o \ |
Makefile中使用变量
在makefile中使用变量类似于C/C++中的宏。
定义一个变量objects
1 | objects = main.o kbd.o command.o display.o |
则可以在makefile中以$(objects)
的方式来使用这个变量。
1 | objects = main.o kbd.o command.o display.o \ |
隐晦规则
make可以自动识别和推倒指令。只要make看到一个 .o
文件,它就会自动的把 .c
文件加在依赖关系中,如果make找到一个whatever.o
,那么 whatever.c
就会是 whatever.o
的依赖文件, cc -c whatever.c
就会被推导出来。
1 | objects = main.o kbd.o command.o display.o \ |
.PHONY
表示clean
是个伪目标文件。
由于很多.o
文件都依赖相同的.h
文件,在此之上,可以进一步简化。
1 | objects = main.o kbd.o command.o display.o \ |
清空目标文件的规则
每个Makefile中都应该写一个清空目标文件(.o
和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。
1 | clean : |
更为稳健的做法是:
1 | .PHONY : clean |
.PHONY
表示clean
是一个“伪目标”。
在rm
命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。
Makefile的运行
输入make
命令后,
- 在当前目录下找名字叫“Makefile”或“makefile”的文件;
- 找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件;
- 如果最终的目标文件不存在,或是最终的目标文件所依赖的后面的
.o
文件的文件修改时间要比最终的目标文件新,则执行后面所定义的命令来生成最终的目标文件; - 如果最终的目标文件所依赖的
.o
文件也不存在,那么make会在当前文件中找目标为.o
文件的依赖性,如果找到则再根据那一个规则生成.o
文件; - make会生成
.o
文件,然后再用.o
文件生成make的最终的目标文件。
如果target与第一个目标文件没有任何关联(如clean),则该命令不会执行。如要执行clean,则需另外输入make clean
命令。