Linux环境基础开发工具->make/Makefile
引入:make/Makefile是什么?
前面我们知道了vim负责编辑代码,gcc负责编译代码,而make/Makefile则负责的是自动化编译!
Makefile是一个文件,make是一条指令
我们在Makefile文件中进行编辑,让哪些文件需要先编译,哪些文件需要后编译
然后简单的make指令即可让整个工程完全自动编译,极大的提高了软件开发的效率
总结:make是一条命令,Makefile是一个文件,两个搭配使用,完成项目自动化构建。
一:需要makefile的场景
现在我想自己实现一个打印功能的函数,所以我有以下三个文件
1:print.h
#pragma once
#include<stdio.h>void print(const char* str);
2:print.c
#include"print.h"
void print(const char* s)
{printf("%s", s);}
3:main.c
#include"print.h"
int main()
{print("hello!");return 0;
}
三个文件:
此时我想形成一个可执行文件,每次执行该文件的时候,就显示出"hello!"
所以现在我们让print.c和main.c通过gcc形成了.o文件
然后再gcc main.o print.o 生成可执行程序a.out
但是太麻烦了,我们这还仅仅只有两个.c文件的合并,以后一个项目都是几十个,难道我们要进行几十次的gcc -c吗??所以我们只需将这些写进makefile这个文件即可
二:makefile的使用演示
1: 在源文件所在目录下创建一个名为Makefile/makefile的文件
2:编写Makefile文件
3:运行make指令
此时发现其自动创建了我们想要的两个.o文件,并且对两个进行链接生成了a.out:
运行a/out
makefile的出现,让我们不在需要手动的每次都gcc ,我们写在makefile中,想用直接make即可!
三:makefile的格式
Makefile文件最简单的编写格式是:
先写出文件的依赖关系,然后写出这些文件之间的依赖方法,依次写下去:
目标: 依赖文件1 依赖文件2 ...Tab键开头的依赖方法
注意:依赖方法最前面必须是tab键,不能连续的空格!
Q:为什么写了makefile,我们进行make就能执行这一系列的指令?
A:make的原理如下:
①:make会在当前目录下找名字为“Makefile”或“makefile”的文件。
②:如果找到,它会找文件当中的第一个目标文件,在上面的例子中,它会找到mytest这个文件,并把这个文件作为最终的目标文件。
③:如果mytest文件不存在,或是mytest所依赖的后面的test.o文件和main.o文件的文件修改时间比mytest文件新,那么它就会执行后面的依赖方法来生成mytest文件。
④:如果mytest所依赖的test.o文件不存在,那么make会在Makefile文件中寻找目标为test.o文件的依赖关系,如果找到则再根据其依赖方法生成test.o文件(类似于堆栈的过程)。
⑤:当然,你的test.c文件和main.c文件是存在的,于是make会生成test.o文件和main.o文件,然后再用test.o文件和main.o文件生成最终的mytest文件。
⑥:make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
⑦:在寻找的过程中,如果出现错误,例如最后被依赖的文件找不到,那么make就会直接退出,并报错。
四:项目清理->clean
在我们每次重新生成可执行程序前,都应该将上一次生成可执行程序时生成的一系列文件进行清理,但是如果我们每次都手动执行一系列指令进行清理工作的话,未免有些麻烦,因为每次清理时执行的都是相同的清理指令,所以makefile也提供了clean板块来进行文件的清理
写法如下:
make clean指令即可清理指定文件:
解释:执行make clean指令之后,按照makefile文件中的指令把两个.o和a.out都删除了!
注意:clean: 是一个依赖关系,只不过clean:不依赖于任何的文件,所以:后面什么都没有
五:ACM
Q1:我重复的进行make会怎么样?
A1:
解释:第一次make生效了,但是第二次打印:`a.out' is up to date. (译为a.out已经是最新的了!)
Q2:系统怎么知道a.out是最新的了?
A2:因为文件的最近修改时间没有变,所以系统对之后的make不作处理!
任何一个文件都有三个时间,简称ACM!
A:最近访问时间
C:最近的属性修改时间
M:最近的内容修改时间
系统在执行make指令的时候,会去比对所涉及的文件的ACM是否发生变化,变化了,此时的make指令才有意义,系统才会进行一些列的gcc指令!
在我们连续的make期间,既没有影响到任何一个文件的最近访问时间,最近属性修改时间,最近内容修改时间,所以最后发现没有任何的变化,所以对第二次及后面的make不做处理!
六:.PHONY
.PHONY
的作用:
将目标(如
clean
)标记为 伪目标,表明它不生成同名文件。当调用伪目标时,Make 跳过对该目标文件存在性的检查,直接执行其命令。
所以我们的clean常常用.PHONY修饰
格式如下:
现在 我们连续的make clean,系统也不会去比对文件的ACM了!
七:makefile的简写方式
Makefile文件的简写方式:
自动变量:
$@:目标文件(如 a.out 或 print.o)。
$^:所有依赖文件(如 print.o main.o)。
$<:第一个依赖文件(如 print.c)。
模式规则:
%.o: %.c:匹配所有 .c 文件生成对应的 .o 文件,避免为每个 .o 文件写重复规则。
通配符清理:
rm -f *.o:删除所有 .o 文件,无需手动列出。
所以可以 简写成如下:
八:一些细节
1:makefile的执行顺序
当你把clean板块放在makefile文件的上面是,此时直接运行 make
时会执行 clean!此时只能进行
make a.out,才能执行 a.out
的构建规则
2:ACM这三个时间可以通过stat指令来查看
需要注意的是,尽管能影响的A的是访问,也就是cat或者touch,但是由于优化,只有第一次的cat能够影响A,后面连续的cat不会改变A!
但是touch指令就算什么都不做,也能够一次性影响ACM三个时间,且能够连续的影响,如图:
3: 为什么要给clean加上.PHONY
-
无
.PHONY
时:
Make 会检查clean
的依赖文件和它本身的时间戳,但这种检查对伪目标毫无意义(例如clean
本就不该对应任何文件)。 -
有
.PHONY
时:
Make 会跳过文件检查,直接执行命令,减少不必要的计算。
总结:提高性能
4:makefile和Makefile都一样,看个人喜好
5:makefile中也能进行宏替换
CC := gcc # 定义编译器
CFLAGS := -Wall # 定义编译选项
TARGET := a.out # 定义目标文件名
好处:修改编译器或选项时只需修改变量值。