文章目录
一、makefile快速开始
1. 什么是makefile、make
Makefile是一个名为make
软件所需要的脚本文件,该脚本文件可以指导make软件控制gcc等工具链去编译工程文件最终得到可执行文件,几乎所有的Linux发行版都内置了GNU-Make软件,VScode等多种IED也内置了Make程序。
你见到的xxx.mk文件或者Makefile都统称为Makefile脚本文件。大多数的make都支持“makefile”和“Makefile”这两种默认文件名称。
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,它解释Makefile 中的指令。Makefile用来调用各种命令做自动化构建工具非常方便。
1.1 make命令 工作流程
- 读入所有的Makefile。
- 读入被include的其它Makefile。
- 初始化文件中的变量。
- 推导隐晦规则,并分析所有规则。
- 为所有的目标文件创建依赖关系链。
- 根据依赖关系,决定哪些目标要重新生成。
- 执行生成命令。
当我们在执行 make 条命令的时候,make 就会去当前文件下找要执行的编译规则,也就是 Makefile 文件。我们编写 Makefile 的时可以使用的文件的名称 “GNUmakefile” 、“makefile” 、“Makefile” ,make 执行时回去寻找 Makefile 文件,推荐使用Makefile。
1.2 关于程序的编译和链接
一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。
编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(LibraryFile),也就是 .lib文件,在UNIX下,是Archive File,也就是.a文件。
总结: 源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)。
2. 掌握 Makefile 常用语法基础
2.1 Makefile 规则语法
规则:用于说明如何生成一个或多个目标文件
规则的格式:
targets:prerequisites
command
目标: 依赖
[tab键]命令
-
target 目标
可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(伪目标)。 -
prerequisites
生成该target所依赖的文件或target。生成目标所要依赖的文件,可能不止一个。 -
command
该target要执行的命令(任意的shell命令)。生成目标所要执行的命令,注意所有的命令必须要以tab 键开头。
总结:target这一个或多个的目标文件依赖于prerequisites中的文件, 其生成规则定义在command中。
2.2 伪目标 | Makefile中.PHONY的作用
.PHONY(伪目标)
伪目标是这样一个目标:它不代表一个真正的文件名,在执行make时可以指定这个目标来执行所在规则定义的命令,有时也可以将一个伪目标称为标签。伪目标通过PHONY来指明。
如果我们指定的目标不是创建目标文件,而是使用makefile执行一些特定的命令,例如:
clean:
rm *.o temp
我们希望,只要输入make clean
后,rm *.o temp
命令就会执行。但是,当前目录中存在一个和指定目标重名的文件时,例如clean文件,结果就不是我们想要的了。输入make clean
后,rm *.o temp
命令一定不会被执行。
解决的办法:将目标clean定义成伪目标就成了。 无论当前目录下是否存在clean
这个文件,输入make clean
后,rm *.o temp
命令都会被执行。
这种做法的带来的好处还不止此,它同时提高了make的执行效率,因为将clean定义成伪目标后,make的执行程序不会试图寻找clean的隐含规则。
举例如下:
clean:
rm -rf $(CLEAN) $(BINS)
.PHONY: clean
2.3 变量赋值
Makefile变量的定义和使用
参考URL: http://c.biancheng.net/view/7096.html
变量的使用在我们的 Makefile 编写中还是非常广泛的,可以说我们的 Makefile 中必不可少的东西。
Makefile 的变量的四种基本赋值方式:
- 简单赋值 ( := ) 编程语言中常规理解的赋值方式,只对当前语句的变量有效。
- 递归赋值 ( = ) 赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。
- 条件赋值 ( ?= ) 如果变量未定义,则使用符号中的值定义变量。如果该变量已经赋值,则该赋值语句无效。
- 追加赋值 ( += ) 原变量用空格隔开的方式追加一个新值。
- 简单赋值
x:=foo
y:=$(x)b
x:=new
test:
@echo "y=>$(y)"
@echo "x=>$(x)"
:=与我们实际上更符合我们平时写代码的思维,也就是顺序声明变量,使用这种赋值方式,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。
比较常用。
- 递归赋值
x=foo
y=$(x)b
x=new
test:
@echo "y=>$(y)"
@echo "x=>$(x)"
结果:
[root@centos7 ~]# make test
y=>newb
x=>new
使用= 需要注意2点:
1)这种赋值方式是有先后顺序的,后面的赋值会覆盖掉前面的赋值。因此这种赋值方式,比较好用。不太符合我们的书写逻辑。
2) 右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的值。
x = $(y)
y = hello
- 条件赋值
x:=foo
y:=$(x)b
x?=new
test:
@echo "y=>$(y)"
@echo "x=>$(x)"
- 追加赋值
x:=foo
y:=$(x)b
x?=new
test:
@echo "y=>$(y)"
@echo "x=>$(x)"
- 追加赋值