make是一个命令
makefile是一个文件
我们现在创建了一个文件叫做test.c
[kk@hcss-ecs-28de lesson8]$ touch test.c
[kk@hcss-ecs-28de lesson8]$ ll
total 0
-rw-rw-r-- 1 kk kk 0 Jan 1 13:28 test.c
[kk@hcss-ecs-28de lesson8]$ vim test.c
[kk@hcss-ecs-28de lesson8]$ cat test.c
#include<stdio.h>
int main()
{
printf("新年快乐");
return 0;
}
[kk@hcss-ecs-28de lesson8]$ 
我们再在这个这个同级目录下创建一个文件Makefile/makefile
我们这里使用大写的字母开头的这个文件
[kk@hcss-ecs-28de lesson8]$ touch Makefile
[kk@hcss-ecs-28de lesson8]$ ll
total 4
-rw-rw-r-- 1 kk kk 0 Jan 1 13:34 Makefile
-rw-rw-r-- 1 kk kk 72 Jan 1 13:29 test.c
[kk@hcss-ecs-28de lesson8]$ 
创建好这个Makefile之后我们使用vim打开这个文件
我们输入
mytest:test.c
gcc test.c -o mytest我们形成的目标可执行文件叫做metest,依赖的是test.c
方法叫做gcc test.c -o mytest
但是下面我们应该如何形成可执行文件呢?
我们直接输入命令make就行了
[kk@hcss-ecs-28de lesson8]$ make
gcc test.c -o mytest
[kk@hcss-ecs-28de lesson8]$ ll
total 20
-rw-rw-r-- 1 kk kk 37 Jan 1 13:36 Makefile
-rwxrwxr-x 1 kk kk 8360 Jan 1 13:39 mytest
-rw-rw-r-- 1 kk kk 72 Jan 1 13:29 test.c
[kk@hcss-ecs-28de lesson8]$ 
那么这个就是make/makefile
make就是一个命令,我们可以使用which进行命令位置的探查的操作
[kk@hcss-ecs-28de lesson8]$ which make
/usr/bin/make
[kk@hcss-ecs-28de lesson8]$ 这个命令在/usr/bin/目录下
makefile就是我们当前文件夹里面的一个文件

我们这里的第一行的mytest就是我们的目标生成文件
test.c就是我们的依赖文件
就是我们想拿一个test.c去形成我们的mytest文件
那么我们将我们的第一行,以冒号隔开的这一行视作为依赖关系行
第二行就是如何将我们的test.c编译成mytest文件
我们将第二行叫做依赖方法
那么这个核心思想就是通过我们的依赖关系和依赖方法形成目标文件
依赖关系和依赖方法必须同时存在才有效
那么我们既然能进行程序文件的生成操作,那么就能进行删除,那么我们该怎么进行清理的操作呢?

我们第一行的冒号左边的mytest是目标文件
右边的是依赖文件,如果文件多的话那么我们称之为依赖文件列表
这个目标文件和依赖文件列表合起来就是依赖关系
make会自定向下扫描makefile文件,默认形成第一个目标文件
如果想指定形成目标文件的话,make targetname
如果我们是向下面那样的话,那么我们默认生成的就是清理的这个程序


我们发现我们这里的make一次之后生成对应的文件之后我们就不能进行第二次的make了,但是我们的make clean是可以的
因为我们的test.c没有被修改,我们的可执行程序的源文件没有进行任何的修改,那么就没有重新编写的必要了
但是我们在原本的第一行前面加上我们的.PHONY:mytest
那么我们从上面扫描下来第一个生成的还是这个mytest

然后我们进行命令的执行

那么就说明我们的.PHONY冒号后面修饰的伪目标代表的含义:
所依赖的方法:总是被执行的!
说白了,凡是用.PHONY修饰的伪目标它的依赖方法和依赖关系总是会被执行的
因为我们的清理总是需要进行执行的,所以我们这里的clean是需要被.PHONY进行修饰操作的
为什么没有添加.PHONY修饰的文件不能多次进行make操作呢?
主要还是提高编译效率
对比源文件和可执行文件的修改时间
如果源文件的修改时间比可执行文件要新的话,那么就说明我们这个源文件修改过
如果源文件的修改时间比可执行文件时间要旧的话,那么就说明我们这个源文件没有进行修改过
我们可以使用命令stat test.c进行对可执行文件的相关时间进行查看的操作
上面的是我们原始的,下面是我们更改过源文件的

属性和内容都会进行变化的
我们使用chmod进行文件权限的改变
那么我们这里的Change的时间是会更新的

如果我们将内容进行改变的话,那么就是Modify进行改变
如果改变的是属性的话,那么就改变的是change
如果我们使用cat等命令对文件内容进行访问操作的话,那么我们的Access就会改变了
但是我们多查几次,时间又不变化了
现在的话只要累计访问这个文件多少次才能进行更新的操作
Access变的不是实时更新了,有效的减少io的次数
如果我们要进行文件时间更新的操作的话,我们可以使用touch filename进行文件的时间的更新
touch不仅能进行文件的创建操作,而且也能进行文件对应时间的更新操作

我们这里先进行源文件的编译然后生成可执行文件
然后我们的源文件的时间比可执行文件的时间早

那么为了验证我们之前的理论,就是只要我们的源文件的时间比可执行文件的时间早的话,那么这个就不能make多次

那么我们这里使用touch对源文件进行时间的更新操作 看看是否能进行make操作 那么我们就能再次进行编译操作了

那么到底是否能够重新进行编译的话取决于我们源文件和这个可执行文件的时间前后
那么我们这里重谈:我们的.PYHTON:将我们后面的文件作为伪目标,可以总是被执行的
那么这个是怎么做到的呢?
作用就是忽略我们的对比时间,那么所依赖的方法总是可以被执行的
只要加上了这个.PYHTON:进行修饰操作的话,那么我们的程序编译总是可以被执行的,因为我们忽略了这个对比时间,不会因为我们的源文件比可执行文件的修改时间早而使我们的文件不会进行编译操作
如果我们要在makefile里面进行注释的话我们是在前面加上#进行注释的,和Python一样

我们在这里进行编辑成这样,主要是探究gcc/g++的流程
我们要生成的文件是Mytest文件,依赖的是test.o文件,但是现在我们当前目录中并不存在这个文件,那么我们就往下面去找了
然后我们又发现我们的test.o文件依赖于test.s文件
那么我们又往下面进行寻找的操作
test.s文件依赖于.i文件
.i文件依赖于我们的.c文件
我们的目录中是存在这个test.c文件的
那么我们使用.i对应的方法形成.i
gcc -E test.c -o test.i
然后就一直回溯到我们的mytest那里,生成我们最终的文件mytest

那么这里出现的第一个比较模糊的概念是
make会进行依赖关系的推导,直到依赖文件是存在的

将依赖的方法不断入栈,推导完毕,出栈执行方法!
我们这里的clean的依赖文件是空的
我们文件从.c文件到.o文件的过程我们称之为编译
然后加上一些库文件这个过程就叫做链接

Makefile+ ? ? ?? buffers
1 BIN=mytest
2 SRC=test.c
3 OBJ=mytets.o
4 CC=gcc
5 RM=rm -f
6 $(BIN):$(OBJ)
7 gcc $(OBJ) -o $(BIN)
8 $(OBJ):$(SRC)
9 gcc -c $(SRC) -o $(OBJ)
10
11 .PHONY:clean
12 clean:
13 $(RM) $(BIN) $(OBJ)
14
15 #mytest:mytest.o
16 # gcc mytest.o -o mytest
17 #mytest.o:test.c
18 # gcc -c test.c -o mytest.o
19 #
20 #.PHONY:clean
21 #clean:
22 # rm -f *.o mytest
我们这里将文件名以及命令的操作用变量进行表示
类似于我们的宏定义
那么下面我们直接调用变量就行了
如果我们需要进行别的文件的修改操作的话
那么我们直接将这个变量指向的名字进行修改就行了
[kk@hcss-ecs-28de lesson8]$ make
gcc -c test.c -o mytets.o
gcc mytets.o -o mytest
[kk@hcss-ecs-28de lesson8]$ 
这样也是照样可以进行正常的编译操作的
我们也是可以进行正常清理操作的
[kk@hcss-ecs-28de lesson8]$ make clean
rm -f mytest mytets.o
[kk@hcss-ecs-28de lesson8]$ ll
total 12
-rw-rw-r-- 1 kk kk 73 Jan 1 22:20 !
-rw-rw-r-- 1 kk kk 611 Jan 2 12:00 Makefile
-rw-rw-r-- 1 kk kk 72 Jan 1 13:29 test.c
[kk@hcss-ecs-28de lesson8]$ 
所以整个Makefile可以使用变量的方式进行代换了
但是这么写的话代码还不是很通用的
如果我们存在很多的文件呢?等着我们去编译的话
我们这里使用命令touch test{1..100}.c进行100个文件的创建操作
然后就能看到我们的这些文件了

我们现在要将这么多源文件编译形成一个可执行的文件
所以我们的原先的Makefile就不行了
我们就得进行修改的操作了

我们这里将当前文件夹里面的所有.c文件都罗列出来,然后将这些文件都放在SRC这个变量里面
我们扫描的时候直接扫描这个SRC文件夹里面
我们先将这个SRC后面的内容注释掉
我们进行测试
我们在后面
.PHONY:test
8 test:
9 echo $(BIN)
10 echo $(SRC)

我们回到终端我们可以发现我们执行的命令是echo $(BIN)
然后我们回到vim模式进行编辑操作
我们对SRC进行编辑的操作
将所有的.c文件都筛选出来
我们SRC后面的是一个命令,我们先进行命令的执行操作

最后我们测试下我们能否将所有的.c文件都打印出来
使用make命令发现是可行的,确实是可以提取出我们的所有的.c文件

我们可以发现是两行,中间用空格间隔开了
这是因为gcc会将命令进行回显的操作
如果我们单纯的想要我们的这里不出现我们的回显内容,我们只是将我们的结果打印出来
我们可以在vim中进行编辑的操作
我们可以直接在我们要执行的命令的前面加上我们的@就可以不用进行呢回显的操作了


我们将当前文件里面的.c文件都拿到了
利用shell ls *.c将文件都进行获取了
我们这里的变量SRC
我们是先将后面的命令进行执行了, 然后将这个结果赋值给SRC
那么我们就能动态的拿到当前目录下的.c文件了

当然了,除了我们的shell命令
我们还能使用wildcard将我们文件夹内的.c文件动态的找出来
所以shell ls *.c和SRC=$(wildcard *.c)是等价的
我们保存退出照样能动态的拿到所有的.c文件
我们期望将我们的源文件变成.o文件,再进行链接的操作
所以我们需要将OBJ这个变量进行改变的操作
我们将SRC中所有的.c文件变成.o文件形成OBJ
OBJ=$(SRC:.c=.o) 
那么我们这里再次进行测试的操作

我们从下面可以发现我们之前的.c文件都被替换成了.o文件了
主要是通过命令$(SRC:.c=.o)

那么我们现在将我们的.c和.o文件都拿到了
那么下面就是正常的代码编写了

这里我们的$^表示的是我们依赖关系中的依赖关系列表
就是我们上面的OBJ ,文件夹内的所有.o文件
$@就是我们的目标文件,这里的BIN
就是下面的意思

简单点来就是我们利用gcc将这里的
@

我们这里将.c文件变成.o文件
%.o:%.c #这里的%是通配符,表示的是匹配愿意内容,.c就是匹配任意.c结尾的> 文件
12 $(CC) -c $< 
我们将展开的文件,就是.c文件展开
我们上面的%.c文件依赖关系会被一个一个进行展开的操作
而我们的$<的意思是将我们上面展开的多个.c文件交给对应的命令
一个一个的形成.o文件
<表示的是将.c文件一个个的拿出来经过左边的方法$(CC)加工成.o文件
那么我们经过这两行命令可以将.o文件变成我们的.o文件了
那么我们的.o文件有了
那么我们的OBJ文件列表就有了
然后再将我们的OBJ文件变成可执行的文件BIN就行了

最后形成的可执行的文件叫做mytest文件

那么最终我们的Makefile就是这个了
BIN=mytest
2 #SRC=$(shell ls *.c)#将当前文件夹内的.c文件都罗列出来,然后将这些文件都> 放在SRC这个变量里面
3
4 SRC=$(wildcard *.c)#将当前文件夹内的.c文件都罗列出来,然后将这些文件都放 在SRC这个变量里面
5 OBJ=$(SRC:.c=.o)
6 CC=gcc
7 RM=rm -f
8
9 $(BIN):$(OBJ)#我们生成的可执行文件依赖的是.o文件,就是OBJ
10 $(CC) $^ -o $@ #通过gcc将我们的OBJ文件编译成BIN文件
11 @echo "链接 $^ 成 $@"
12 %.o:%.c #这里的%是通配符,表示的是匹配愿意内容,.c就是匹配任意.c结尾的> 文件
13 $(CC) -c $<
14 @echo "编译...$< 成 $@"#我们将.c文件编译成.o文件
15 #$(BIN):$(OBJ)
16 # gcc $(OBJ) -o $(BIN)
17 #$(OBJ):$(SRC)
18 # gcc -c $(SRC) -o $(OBJ)
19 #
20 .PHONY:test
21 test:
22 @echo $(BIN)
23 @echo $(SRC)
24 @echo $(OBJ)
25
26 .PHONY:clean
27 clean:
28 @$(RM) $(BIN) $(OBJ) $(SRC) 
下面是本来的意思,可以对比看看
