1.概念
make 是一条命令 , Makefile/makefile是一个文件。 【 makefile 是一个 写了如何编译文件,形成可执行程序的文件】
2. 语法
1. 基本语法
依赖关系 // 依赖关系 由 目标名和依赖文件列表组成,语法为 目标名 : 依赖文件列表
【Tab】依赖方法
此处的 test : test.c 是依赖关系, test 的依赖文件是 test.c ,此处依赖文件列表中只有test.c一个依赖文件
【Tab】gcc -o test test.c 此处的 gcc -o test test.c 是依赖方法
-
形成空依赖关系的方法:
此处的 .PHONY: 是一个语法规定,类似于关键字。
.PHONY: 自定义的伪目标的目标名
.PHONY 会使得依赖方法忽略掉时间.
目标名:
【Tab】 依赖方法
此处的 clean: 是一个空依赖关系
【Tab】 rm test 此处的 rm test 是依赖方法
-
依赖方法可以是任意指令:
Make默认会把回显执行的命令,@符号关闭命令回显
Eg:
(4) % , $<
%.是Makefile/makefile中的通配符。
%.c 是把当前目录下的.c文件 展开到依赖列表中
$< 是 把 依赖关系中的 依赖文件 一个一个的交给 gcc -c 选项,形成同名的 .o 文件
5. Makefile/makefile中也能定义变量。 $(变量名)使用变量 , $^ , $@
Makefile/makefile中定义变量不用变量类型。 $(变量名) 是把变量名展开。$^是所有依赖文件列表, $@是目标文件
如下图:
$(bin):$(src) => 相对于 proc : proc.c
此次的 $^ => 相当于 $(src) 即, proc.o
此处的 $@ => 相当于 $(bin) 即 proc
3. Makefile/makefile 和 make 的基本原理
(1). Makefile/makefile 的文件名,会被make从上往下扫描, 第一个目标名是缺省要形成的。如果我们想执行其他组的依赖关系和依赖方法,可以通过 make 目标名实现,eg:make name
(2) make Makefile/makefile 在执行gcc命令的时候,如果发生语法错误,就会终止推导过程
Eg: 显然 由于test.c中存在语法错误,因此编译test.c的命令执行完产生报错无法形成可执行程序,终止了推导过程。 具体表现在此处生成 test 且 没有 执行 each “编译结束…”命令
(3) make 解释 Makefile/makefile 的时候 是 会自动推导的, 推导原则是一直推导,推导过程 不执行依赖方法。直到 推导到有依赖文件存在,然后再逆向的执行所有的依赖方法。如下图
-
make 默认只形成一个可执行程序。 使make形成多个可执行程序的方法
如果想形成两个可执行程序, 那么 .PHONY: 一个伪目标,该伪目标的 依赖文件列表 是 两个未形成的依赖文件, 因此该依赖方法进栈, 然后 推导,直到推导到有依赖文件存在,然后逆向的执行所有的依赖方法。
all : $(bin1) $(bin2) ,但是bin1 , bin2 并未不存在,那么向下推导,$(bin1) : $(src1) 依赖关系,然后执行 对应的 依赖方法,从而形成bin1。 然后继续向下推导, $(bin2): $(src2) 依赖关系,然后执行对应的依赖方法,从而形成 bin2 。 此时 bin1 和 bin2 文件都有了,且 栈顶 为 all : $(bin1) $(bin2) 的依赖方法,因此执行该依赖方法,而此依赖方法恰好为空而已
4. 为什么有时候程序需要重新编译,有时候不需要? 为什么?
对于源文件和可执行程序-> 两者都是文件
文件 = 内容 + 属性
时间也是文件的属性
查看文件时间: stat 文件名
文件内容最新被修改的时候是 Modify
文件属性最新被修改的时间是Change
Access时间是文件最新的访问时间,但是实际上现在已经是 当访问次数达到一定量的时候才会对 access时间进行变化。
修改文件的属性时,只有文件的 change 时间发生变化, 但是有些命令在改变文件属性同时对文件的modify时间进行改变,不过这种改变并不是因为文件属性被改变而导致的
修改文件的内容时,文件的modify会发生改变,change时间可能发生改变。原因是当文件内容被修改的时候 可能会导致文件的属性也发生修改。(毕竟 文件大小也是文件属性)。
总结:文件的创建时间也就是modify时间。 程序有时候需要重新编译,有时候不需要的原因是 当源文件的modify时间 大于 编译生成后的可执行程序的modify时间的时候,会对源文件进行重新编译, 当源文件的modify时间小于之前生成的可执行程序的modify时间的时候 不会对源文件的modify文件进行重新编译。
.PHONY: 会使得依赖方法忽略掉时间。 因此如下图时,会总是对test.c 进行编译,因为
.PHONY: 使得下文的依赖方法忽略掉了时间,即: test.c 的modify时间 和 test 的modify的时间对比被忽略了。