1.实验目的与要求
- 使用Makefile来管理编译源码,掌握Makefile的基本语法。
- 学习Makefile中的变量的定义和使用,以及常用的特殊变量使用。
- 学习makefile的自动推导功能。
- 学习使用makefile的伪目标。
2.实验平台
实验室安装的实验环境(Linux操作系统)和头歌(www.educoder.net)实验平台(课程实验)
3.实验内容
- Makefile的基本语法。
- Makefile中的变量的定义和使用
- makefile的自动推导功能
- makefile的伪目标
4.实验详细内容、步骤
任务描述
本关任务:使用Makefile
来管理编译源码,掌握Makefile
的基本语法。
相关知识
什么是makefile
?或许很多Winodws
的程序员都不知道这个东西,因为那些Windows
的IDE
都为你做了这个工作,但是要作一个专业的程序员,makefile
还是要懂的。makefile
其实就是描述了整个工程中所有文件的编译顺序,编译规则,并且由make
命令来读取makefile
文件,然后根据makefile
文件中定义的规则对其进行解析,完成对整个项目的编译操作。
makefile
在linux
操作系统中是比较常见的,例如,我们在使用源码安装一个软件的时候,通常只需执行make
命令即可完成对软件的编译,正是因为软件开发者已经编写了makefile
文件,所以只需执行make
命令就会完成对整个工程的自动编译。
本关将介绍makefile
的语法,使用makefile
来完成对软件的编译。
Makefile规则
makefile
文件中包含了一组用来编译应用程序的规则,一项规则可分成三个部分组成:
- 工作目标(target)
- 依赖条件(prerequisite)
- 所要执行的命令(command)
格式为:
- target : prereq1 prereq2
- commands
以上格式就是一个文件的依赖关系,也就是说,target
这个目标文件依赖于多个prerequisites
文件,其生成规则定义在commands
中。说白一点就是说,prerequisites
中如果有一个以上的文件比target
文件要新的话,commands
所定义的命令就会被执行。这就是Makefile
的规则。也就是Makefile
中最核心的内容。
注意
- commands前面使用的是TAB键,而不是空格,使用空格会出现错误;
- commands可以是任意的shell命令;
- 在执行make命令时,make会解析第一项规则;
案例演示1:
存在一个源码文件main.c
文件,编译一个makefile
规则来编译该文件,并生成一个名为HelloWorld
的可执行文件,具体操作如下:
- vim makefile
- make
- 使用
vim
编写如下代码
- #include <stdio.h>
- int main()
- {
- printf("Hello world\n");
- return 0;
- }
- 使用
vim
编写makefile
- HelloWorld : main.c
- gcc -o HelloWorld main.c
[请在右侧“命令行”里直接体验]
通过以上案例可以看到,编写好makefile
后,只需要输入make
命令即自动只需定义好的规则。
注意:gcc -o HelloWorld main.c
命令前是TAB
键而不是空格。
案例演示2:
假设一个项目中包含5
个源码文件,分别是Add.c
、Sub.c
、Mul.c
、Div.c
和main.c
和一个头文件def.h
,编译一个makefile
规则来编译该项目,并生成一个名为exe
的可执行文件,具体操作如下:
- vim makefile
- make
- vim Add.c
- #include <stdio.h>
- int Add(int a, int b)
- {
- return a + b;
- }
- vim Sub.c
- #include <stdio.h>
- int Sub(int a, int b)
- {
- return a - b;
- }
- vim Mul.c
- #include <stdio.h>
- int Mul(int a, int b)
- {
- return a * b;
- }
- vim Div.c
- #include <stdio.h>
- int Div(int a, int b)
- {
- return a / b;
- }
- vim main.c
- #include <stdio.h>
- #include "def.h"
- int main()
- {
- int add = Add(10, 5);
- int sub = Sub(10, 5);
- int mul = Mul(10, 5);
- int div = Div(10, 5);
- printf("10 + 5 = %d\n", add);
- printf("10 - 5 = %d\n", sub);
- printf("10 * 5 = %d\n", mul);
- printf("10 / 5 = %d\n", div);
- return 0;
- }
- vim def.h
- #ifndef __DEF_H__
- #define __DEF_H__
- #include <stdio.h>
- int Add(int a, int b);
- int Sub(int a, int b);
- int Mul(int a, int b);
- int Div(int a, int b);
- #endif
- vim makefile
- exe : main.o Add.o Sub.o Mul.o Div.o
- gcc -o exe main.o Add.o Sub.o Mul.o Div.o
- main.o : main.c def.h
- gcc -c main.c -o main.o
- Add.o : Add.c
- gcc -c Add.c -o Add.o
- Sub.o : Sub.c
- gcc -c Sub.c -o Sub.o
- Mul.o : Mul.c
- gcc -c Mul.c -o Mul.o
- Div.o : Div.c
- gcc -c Div.c -o Div.o
[请在右侧“命令行”里直接体验]
以上案例,当只需make
命令时,首先解析目标为exe
的规则,然后发现exe
依赖于main.o
、Add.o和Sub.o
,然后分别对main.o
、Add.o和Sub.o
规则进行解析,即分别执行目标为main.o
、Add.o和Sub.o
的命令。当main.o
、Add.o和Sub.o
生成后,最后执行exe
对应的命令。
编程要求
本关任务是学会使用makefile
来编译项目。
具体编程要求如下:
- 编写
5
个源文件Add.c、Sub.c、Mul.c、Div.c和main.c和一个头文件def.h,文件内容同案例2
; - 使用
makefile
管理以上项目,并编译生成一个名为Calc
的可执行文件;
- vim Add.c
1. #include <stdio.h>
2. int Add(int a, int b)
3. {
4. return a + b;
5. }
- vim Sub.c
1. #include <stdio.h>
2. int Sub(int a, int b)
3. {
4. return a - b;
5. }
- vim Mul.c
1. #include <stdio.h>
2. int Mul(int a, int b)
3. {
4. return a * b;
5. }
- vim Div.c
1. #include <stdio.h>
2. int Div(int a, int b)
3. {
4. return a / b;
5. }
- vim main.c
1. #include <stdio.h>
2. #include "def.h"
3. int main()
4. {
5. int add = Add(10, 5);
6. int sub = Sub(10, 5);
7. int mul = Mul(10, 5);
8. int div = Div(10, 5);
9.
10. printf("10 + 5 = %d\n", add);
11. printf("10 - 5 = %d\n", sub);
12. printf("10 * 5 = %d\n", mul);
13. printf("10 / 5 = %d\n", div);
14.
15. return 0;
16. }
- vim def.h
1. #ifndef __DEF_H__
2. #define __DEF_H__
3.
4. #include <stdio.h>
5.
6. int Add(int a, int b);
7. int Sub(int a, int b);
8. int Mul(int a, int b);
9. int Div(int a, int b);
10. #endif
- vim makefile
1. Calc : main.o Add.o Sub.o Mul.o Div.o
2. gcc -o Calc main.o Add.o Sub.o Mul.o Div.o
3.
4. main.o : main.c def.h
5. gcc -c main.c -o main.o
6.
7. Add.o : Add.c
8. gcc -c Add.c -o Add.o
9.
10. Sub.o : Sub.c
11. gcc -c Sub.c -o Sub.o
12.
13. Mul.o : Mul.c
14. gcc -c Mul.c -o Mul.o
15.
16. Div.o : Div.c
17. gcc -c Div.c -o Div.o
任务描述
本关任务:学习Makefile
中的变量的定义和使用,以及常用的特殊变量使用。
相关知识
变量的使用可以方便我们编写makefile
文件,我们可以使用一个简短的变量来替换一个比较长的字符串,这样我们在使用这个字符串的时候就可以方便的使用变量来替换。除此之外,当我们修改某一个字符串时,如果我们没有使用变量,则我们需要修改使用该字符串的每个地方,如果使用了变量,我们只需要修改变量定义处即可。
makefile
变量的命令可以包含字符、数字、下划线(可以是数字开头),并且大小写敏感。
makefile
变量在声明时需要对其进行赋值,而在使用该变量时需要在变量名前加上**$
符号 例如$(VARNAME)
,如果用户需要在makefile
文件中使用真实的$
字符,则使用$$
**表示。
makefile
中对变量的赋值方式有三种,分别是:
- 递归赋值(=):递归赋值,即赋值后并不马上生效,等到使用时才真正的赋值,此时通递归找出当前的值;
- 直接赋值(:=):是将":="右边中包含的变量直接展开给左边的变量赋值;
- 条件赋值(?=):只有此变量在之前没有赋值的情况下才会对这个变量进行赋值,所有一般用在第一次赋值;
makefile
除了可以自定义变量外,还存在一些系统默认的特殊变量,这些特殊变量可以方便帮助我们快速的编写makefile
文件,例如:$@
、$<和$^
等等。
本关将介绍makefile
的变量的定义和使用方法,以及使用特殊变量来编写makefile
文件。
Makefile 自定义变量
自定义变量格式:
- 递归赋值
变量名 = 变量内容
- 直接赋值
变量名 := 变量内容
- 条件赋值
变量名 ?= 变量内容
变量的使用格式为: $
变量名
或者${
变量名}
或者$(变量名)
案例演示1:
在上一关中案例2
中的项包含了5
个源码文件和一个头文件,如果使用变量来编写makefile
则会显示出比较简洁的格式,具体操作如下:
- vim makefile
- make
- vim makefile
- object=main.o Add.o Sub.o Mul.o Div.o
- exe : $(object)
- gcc -o exe $(object)
- main.o : main.c def.h
- gcc -c main.c -o main.o
- Add.o : Add.c
- gcc -c Add.c -o Add.o
- Sub.o : Sub.c
- gcc -c Sub.c -o Sub.o
- Mul.o : Mul.c
- gcc -c Mul.c -o Mul.o
- Div.o : Div.c
- gcc -c Div.c -o Div.o
[makefile
内容]
[请在右侧“命令行”里直接体验]
可以看到,我们使用object
来表示main.o Add.o Sub.o Mul.o Div.o
,这样我们就可以使用$(object)
来表示以上目标文件,而不是每次输入这5
个目标文件。
Makefile 特殊变量
makefile
常用的特殊变量有:
- $@:表示所有目标;
- $^:表示所有依赖目标的集合,以空格分隔;
- $<:表示依赖目标中第一个目标的名子;
案例演示1:
接着上一个案例中的项目,如果使用特殊变量来编写makefile
则会显示出更加简洁的格式,具体操作如下:
- vim makefile
- make
- vim makefile
- object=main.o Add.o Sub.o Mul.o Div.o
- exe : $(object)
- gcc -o $@ $(object)
- main.o : main.c def.h
- gcc -c $< -o $@
- Add.o : Add.c
- gcc -c $< -o $@
- Sub.o : Sub.c
- gcc -c $< -o $@
- Mul.o : Mul.c
- gcc -c $< -o $@
- Div.o : Div.c
- gcc -c $< -o $@
[请在右侧“命令行”里直接体验]
编程要求
本关任务是学会使用makefile
来编译项目。
具体编程要求如下:
- 编写
5
个源文件Add.c、Sub.c、Mul.c、Div.c和main.c和一个头文件def.h,文件内容同第一关案例2
; - 使用
makefile
的变量来编译生成一个名为VarCalc
的可执行文件;
• vim makefile
1. object=main.o Add.o Sub.o Mul.o Div.o
2. VarCalc : $(object)
3. gcc -o VarCalc $(object)
4.
5. main.o : main.c def.h
6. gcc -c $< -o $@
7.
8. Add.o : Add.c
9. gcc -c $< -o $@
10.
11. Sub.o : Sub.c
12. gcc -c $< -o $@
13.
14. Mul.o : Mul.c
15. gcc -c $< -o $@
16.
17. Div.o : Div.c
18. gcc -c $< -o $@
任务描述
本关任务:学习makefile
的自动推导功能。
相关知识
make
很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个.o
文件后都写上类似的命令。因为,我们的make
会自动识别,并自己推导命令。
只要make
看到一个.o
文件,它就会自动的把.c
文件加在依赖关系中,如果make
找到一个main.o
,那么main.c
就会是main.o
的依赖文件。并且 gcc -c main.c
也会被推导出来,于是,我们的makefile
再也不用写得这么复杂。
本关将介绍makefile
的自动推导功能。
Makefile 自动推导
自动推导格式: 目标 : 其它依赖
案例演示1:
如果使用自动推导模式来编写上一关卡案例中的makefile
,则会有更简洁的格式,具体操作如下:
- vim makefile
- make
- vim makefile
- object=main.o Add.o Sub.o Mul.o Div.o
- exe : $(object)
- gcc -o $@ $(object)
- main.o : def.h
[请在右侧“命令行”里直接体验]
可以看到,我们只需要为main.o
创建一个编译规则,其4
个目标文件则不需要为其创建编译规则,因为make
会自动的为其构造出编译规则。
编程要求
本关任务是学会makefile
中的自动推导。 具体编程要求如下:
- 修改上一关的
makefile
,使用自动推导来编译项目,并生成一个名为AutoCalc
的可执行文件;
• vim makefile
1. object=main.o Add.o Sub.o Mul.o Div.o
2. AutoCalc : $(object)
3. gcc -o AutoCalc $(object)
4.
5. main.o : def.h
任务描述
本关任务:学习使用makefile
的伪目标。
相关知识
每个Makefile
中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。
通常,我们在使用源码安装软件的时候,都会在编译完软件后,执行make install
这个命令来安装软件,或者执行make clean
这个命令清空临时生成的目标文件。以上操作就是利用了makefile
的伪目标。
本关将介绍makefile
的伪目标。
Makefile 伪目标
makefile
使用.PHONY
关键字来定义一个伪目标,具体格式为: .PHONY : 伪目标名称
案例演示1:
为上一关卡案例中的makefile
添加清空临时目标文件标签clean
,具体操作如下:
- vim makefile
- make
- vim makefile
- object=main.o Add.o Sub.o Mul.o Div.o
- exe : $(object)
- gcc -o $@ $(object)
- main.o : def.h
- .PHONY : clean
- clean :
- rm $(object)
[请在右侧“命令行”里直接体验]
可以看到,当我们执行完make
命令后会生成多个临时文件,然后我们执行make clean
命令后,则会将生成的临时文件删除掉,其实执行make clean
命令就是在执行rm main.o Add.o Sub.o Mul.o Div.o
。
案例演示2:
使用另一个格式来清除临时产生的目录文件和不显示删除命令,具体操作如下:
- vim makefile
- make
- vim makefile
- object=main.o Add.o Sub.o Mul.o Div.o
- exe : $(object)
- gcc -o $@ $(object)
- main.o : def.h
- clean :
- @echo "clean object files"
- @rm $(object)
[请在右侧“命令行”里直接体验]
可以看到,当我们执行make clean
命令后,将不会在终端中显示rm main.o Add.o Sub.o Mul.o Div.o
命令。
注意:在命令前加了**@
**符号,则不会把命令原样输出在终端。
编程要求
本关任务是学会makefile
中的伪目标。 具体编程要求如下:
- 为上一关的
makefile
添加自动清理临时文件的伪目标clean
,并生成一个名为AutoCleanCalc
的可执行文件;
• vim makefile
1. object=main.o Add.o Sub.o Mul.o Div.o
2. AutoCleanCalc : $(object)
3. gcc -o AutoCleanCalc $(object)
4.
5. main.o : def.h
6.
7. .PHONY : clean
8.
9. clean :
10. rm $(object)