Linux基础开发工具(make/Makefile篇)
目录
1.自动化构建-make/Makefile
1.背景
2.理解以及基本使用
1.是什么?快速的见一见make/makefile
2.makefile的基本概念---依赖关系,依赖方法
3.自动进行代码编写和清理
4.什么叫做不被执行?
1.默认老代码不做重新编译
2.make怎么知道bin和.c的新旧问题呢?
3.一些不同写法的Makfile以及推导
1.依赖链版Makefile Makefile
2.变量版Makefile Makefile
3.OBJ版Makefile Makefile
4.改进版Makefile Makefile
1.自动化构建-make/Makefile
1.背景
• 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
• 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一 系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至 于进行更复杂的功能操作
• makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全 自动编译,极大的提高了软件开发的效率。
• make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这 个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile 都成为了一种在工程方面的编译方法。
• make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
2.理解以及基本使用
1.是什么?快速的见一见make/makefile
make是一个命令
makefile是一个文件
2.makefile的基本概念---依赖关系,依赖方法
1 code:code.c 依赖关系---只表明了依赖关系
2 gcc -o code code.c 依赖方法---依赖方法
必须有tab键打头这样就会形成makefile
3.自动进行代码编写和清理
先创建一个myproc.c,写入代码,再写一个Makefile文件
然后直接make就可
我们不光想让Makefile进行编译,也想让其进行清理
1 myproc:myproc.c 2 gcc -o myproc myproc.c 3 .PHONY:clean 4 clean: 5 rm -f myproc保存退出后,直接make clean,就会清理了
1 myproc:myproc.c 2 gcc -o myproc myproc.c 3 .PHONY:clean 4 clean: 5 rm -f myproc第三行clean 伪目标---表示总是被执行,对应的依赖关系和依赖方法
4、5行也是一组依赖关系,依赖方法,clean依赖一组空的文件表,那么rm -f就会自动执行
make命令扫描makefile命令的时候,从上往下扫描,默认形成第一个目标文件
所以一般把可执行文件放前面
那么为什么要在第三行clean前面加上.PHONY呢?
.PHONY相当于一个建议的关键字,它的作用是伪目标来表示总是被执行,对应的依赖关系和依赖方法
4.什么叫做不被执行?
1.默认老代码不做重新编译
我们发现第一次编译之后,就不再执行了
那么既然可以修饰clean,自然也可以修饰myproc
1 .PHONY:myproc 2 myproc:myproc.c 3 gcc -o myproc myproc.c 4 .PHONY:clean 5 clean: 6 rm -f myproc
这样被.PHONY修饰就可以总是被执行
如果有很多文件的话,不加.PHONY修饰,那么老文件就可以不被执行不重新编译,只执行修改的文件,也算是提高了效率
2.make怎么知道bin和.c的新旧问题呢?
之前提到过
文件=内容+属性
所以修改一个文件就可以修改内容或者属性要么两个都改
如果只修改文件的内容,我们称为Modify时间被更改
如果只修改文件的属性,我们称为Change时间被修改
如果只是单纯查看文件,Access时间被更新
看Modify时间
[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ stat myproc.c File: ‘myproc.c’ Size: 78 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1061380 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1001/ user1) Gid: ( 1001/ user1) Access: 2026-04-15 16:58:25.727086252 +0800 Modify: 2026-04-15 16:56:36.295258404 +0800 Change: 2026-04-15 16:56:36.295258404 +0800 Birth: -我们修改一下myproc.c
[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ stat myproc.c File: ‘myproc.c’ Size: 108 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1061382 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1001/ user1) Gid: ( 1001/ user1) Access: 2026-04-15 17:30:09.736851047 +0800 Modify: 2026-04-15 17:30:09.736851047 +0800 Change: 2026-04-15 17:30:09.736851047 +0800 Birth: -一般只要改Modify,内容属性都会被改
用chmod可以修改它的Change时间
[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ chmod u+x myproc.c [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ ll total 20 -rw-rw-r-- 1 user1 user1 74 Apr 15 17:21 Makefile -rwxrwxr-x 1 user1 user1 8440 Apr 15 17:21 myproc -rwxrw-r-- 1 user1 user1 108 Apr 15 17:30 myproc.c [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ stat myproc.c File: ‘myproc.c’ Size: 108 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1061382 Links: 1 Access: (0764/-rwxrw-r--) Uid: ( 1001/ user1) Gid: ( 1001/ user1) Access: 2026-04-15 17:30:50.265526796 +0800 Modify: 2026-04-15 17:30:09.736851047 +0800 Change: 2026-04-15 18:22:14.113943566 +0800 Birth: -但是我们发现我们查看一下,然后stat连access时间都没变。不是说会变吗?
这是因为在Linux系统中查找一个内容或者属性比修改的比重要大的多的,这样如果查一下就改一下时间就可能会造成隐性的成本,过于高频把时间更新,系统中会有大量IO操作所以改成查个几十次上百次文件,时间才会改变。
所以通过Modify时间可以知道文件新旧。
touch可以更改时间
[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ ll total 20 -rw-rw-r-- 1 user1 user1 74 Apr 15 17:21 Makefile -rwxrwxr-x 1 user1 user1 8440 Apr 15 17:21 myproc -rw-rw-r-- 1 user1 user1 108 Apr 15 17:30 myproc.c [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ touch myproc.c [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ stat myproc.c File: ‘myproc.c’ Size: 108 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1061382 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1001/ user1) Gid: ( 1001/ user1) Access: 2026-04-15 18:38:01.962633129 +0800 Modify: 2026-04-15 18:38:01.962633129 +0800 Change: 2026-04-15 18:38:01.962633129 +0800 Birth: -可以看到touch一个以存在的文件,三个时间都更改了。
所以说之前.PHONY是因为,make调用gcc时告诉gcc忽略对比时间、忽略新旧问题,直接编译。
3.一些不同写法的Makfile以及推导
1.依赖链版Makefile
依赖链---推导链
Makefile
1 myproc:myproc.o 2 gcc myproc.o -o myproc 3 myproc.o:myproc.s 4 gcc -c myproc.s -o myproc.o 5 myproc.s:myproc.i 6 gcc -S myproc.i -o myproc.s 7 myproc.i:myproc.c 8 gcc -E myproc.c -o myproc.i 9 10 .PHONY:clean 11 clean: * 12 rm -f *.i *.s *.o myprocmake一下,编译过程就体现出来了
[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ make gcc -E myproc.c -o myproc.i gcc -S myproc.i -o myproc.s gcc -c myproc.s -o myproc.o gcc myproc.o -o myproc [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ ll total 48 -rw-rw-r-- 1 user1 user1 315 Apr 15 18:54 Makefile -rwxrwxr-x 1 user1 user1 8440 Apr 15 18:54 myproc -rw-rw-r-- 1 user1 user1 108 Apr 15 18:38 myproc.c -rw-rw-r-- 1 user1 user1 16913 Apr 15 18:54 myproc.i -rw-rw-r-- 1 user1 user1 1560 Apr 15 18:54 myproc.o -rw-rw-r-- 1 user1 user1 479 Apr 15 18:54 myproc.sMakefile在翻译的时候,内部会维护一种栈结构的东西
Makefile文件就会被Makefile依次扫描从上到下,
首先根据依赖关系,比如上面,结果发现myproc.o不存在,就将这个入栈。
然后根据依赖关系继续向下,同理,不断入栈
到最后发现myproc.c有,然后执行这个命令,从而依次出栈并执行命令
从上到下扫描,从下到上递归式地执行形成最终的目标文件
这就是Makefile进行推导的最基本的规则
make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么:
1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到 myproc 这 个文件,并把这个文件作为最终的目标文件。
3. 如果 myproc 文件不存在,或是 myproc 所依赖的后面的 myproc.o 文件的文件修改时间要 比 myproc 这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成 myproc 这个文件。
4. 如果 myproc 所依赖的 myproc.o 文件不存在,那么 make 会在当前文件中找目标为 myproc.o 文件的依赖性,如果找到则再根据那一个规则生成 myproc.o 文件。(这有点像一 个堆栈的过程)
5. 当然,你的C文件和H文件是存在的啦,于是 make 会生成 myproc.o 文件,然后再用 myproc.o 文件声明 make 的终极任务,也就是执行文件 hello 了。
6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个 目标文件。
7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对 不起,我就不工作啦。
所以里面如果不小心写错什么,如下就写错第四行最后o变x
Makefile
1 myproc:myproc.o 2 gcc myproc.o -o myproc 3 myproc.o:myproc.s 4 gcc -c myproc.s -o myproc.x 5 myproc.s:myproc.i 6 gcc -S myproc.i -o myproc.s 7 myproc.i:myproc.c 8 gcc -E myproc.c -o myproc.i 9 10 .PHONY:clean 11 clean: * 12 rm -f *.i *.s *.o myproc从而无法通过
所以说不太推荐这样写,这里只是展示一下编译过程等。
2.变量版Makefile
Makefile
BIN=myproc # 定义变量 BIN,值为 myproc(通常代表最终可执行文件名) CC=gcc # 定义变量 CC,值为 gcc(指定 C 编译器) SRC=myproc.c # 定义变量 SRC,值为 myproc.c(源文件) FLAGS=-o # 定义变量 FLAGS,值为 -o(编译选项,用于指定输出文件) .PHONY: test # 声明 test 是一个伪目标(不生成实际文件) test: # test 目标的规则 @echo $(BIN) # 打印变量 BIN 的值(myproc) @echo $(CC) # 打印变量 CC 的值(gcc) @echo $(SRC) # 打印变量 SRC 的值(myproc.c) @echo $(FLAGS)# 打印变量 FLAGS 的值(-o)在前面加上@就可以变成静默式
可以把这些理解为宏
Makefile
1 BIN=myproc 2 CC=gcc 3 SRC=myproc.c 4 FLAGS=-o 5 RM=rm -f 6 7 8 $(BIN):$(SRC) 9 $(CC) $(FLAGS) $(BIN) $(SRC) 10 .PHONY: 11 clean: 12 $(RM) $(BIN) 13 14 .PHONY:test 15 test: 16 @echo $(BIN) 17 @echo $(CC) 18 @echo $(SRC) 19 @echo $(FLAGS) 20 @echo $(RM)Makefile
1 BIN=myproc 2 CC=gcc 3 SRC=myproc.c 4 FLAGS=-o 5 RM=rm -f 6 7 8 $(BIN):$(SRC) 9 @$(CC) $(FLAGS) $@ $^ 10 @echo "linking ... $^ to $@" 11 .PHONY: 12 clean: 13 @$(RM) $(BIN) 14 @echo "remove ... $(BIN)" 15 16 .PHONY:test 17 test: 18 @echo $(BIN) 19 @echo $(CC) 20 @echo $(SRC) 21 @echo $(FLAGS) 22 @echo $(RM)在前面加上@就可以变成静默式
$@对应依赖的众多文件 $(BIN)
$^对应依赖的目标文件 $(SRC)
3.OBJ版Makefile
1 BIN=myproc.exe 2 CC=gcc 3 SRC=myproc.c 4 OBJ=myproc.o 5 FLAGS=-o 6 RM=rm -f 7 8 $(BIN):$(OBJ) 9 $(CC) $(FLAGS) $@ $^ 10 $(OBJ):$(SRC)如果有几百个-o -c呢?
所以应该改一下
Makefile
1 BIN=myproc.exe 2 CC=gcc 3 SRC=myproc.c 4 OBJ=myproc.o 5 LFLAGS=-o 6 FLAGS=-c 7 RM=rm -f 8 9 $(BIN):$(OBJ) 10 @$(CC) $(LFLAGS) $@ $^ 11 @echo "linking ... $^ to $@" 12 %.o:%.c 13 @$(CC) $(FLAGS) $< 14 @echo "compling ... $< to $@" 15 16 .PHONY:clean 17 clean: 18 $(RM) $(OBJ) $(BIN)在前面加上@就可以变成静默式
%:把当前路径下的所有.o/.c依次展开
$@ - 目标文件 (Target)
$< - 第一个依赖文件 (First prerequisite)
$^ - 所有依赖文件 (All prerequisites)
4.改进版Makefile
1 BIN=myproc.exe 2 CC=gcc 3 SRC=$(shell ls *.c) 4 OBJ=myproc.o 5 LFLAGS=-o 6 FLAGS=-c 7 RM=rm -f 8 9 $(BIN):$(OBJ) 10 @$(CC) $(LFLAGS) $@ $^ 11 @echo "linking ... $^ to $@" 12 %.o:%.c 13 @$(CC) $(FLAGS) $< 14 @echo "compling ... $< to $@" 15 16 .PHONY:clean 17 clean: 18 $(RM) $(OBJ) $(BIN) 19 20 .PHONY:test 21 test: 22 @echo $(SRC) 23 @echo $(OBJ)shell ls *.c 用于列出当前目录下所有以
.c结尾的文件
Makefile自己也有一种这样的函数wildcard
Makefile
1 BIN=myproc.exe 2 CC=gcc 3 #SRC=$(shell ls *.c) 4 SRC=$(wildcard *.c) 5 OBJ=$(SRC:.c=.o) 6 LFLAGS=-o 7 FLAGS=-c 8 RM=rm -f 9 10 $(BIN):$(OBJ) 11 @$(CC) $(LFLAGS) $@ $^ 12 @echo "linking ... $^ to $@" 13 %.o:%.c 14 @$(CC) $(FLAGS) $< 15 @echo "compling ... $< to $@" 16 17 .PHONY:clean 18 clean: 19 $(RM) $(OBJ) $(BIN) 20 21 .PHONY:test 22 test: 23 @echo $(SRC) 24 @echo $(OBJ)在前面加上@就可以变成静默式
%:把当前路径下的所有.o/.c依次展开
$@ - 目标文件 (Target)
$< - 第一个依赖文件 (First prerequisite)
$^ - 所有依赖文件 (All prerequisites)SRC=$(wildcard *.c) ---显示当前目录下所有的.c文件名
OBJ=$(SRC:.c=.o) ---SRC内部的文件名.c ->文件名.o
测试一下
新增一百个文件
le就是小于等于
count=1; while [ $count -le 100 ]; do touch code${count}.c; let count++; donemake
make clean
今天我们make/Makefile的学习就到此结束,期待我们下次再见!(下篇会根据make/Makefile写一个简单的进度条)
