Makefile入门基础

Makefile语法基础

一、Makefile介绍

make 命令执行时,需要一个 Makefile 文件,以告诉 make 命令需要怎么样的去编译和链接程序。

首先,我们要写一个 Makefile 来告诉 make 命令如何编译和链接这几个文件。我们的规则是:
1)如果这个工程没有编译过,那么我们的所有 C 文件都要编译并被链接。
2)如果这个工程的某几个 C 文件被修改,那么我们只编译被修改的 C 文件,并链接目标工程。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的 C 文件,并链接目标程序。

只要我们的 Makefile 写得够好,所有的这一切,我们只用一个 make 命令就可以完成,make 命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。


二、Makefile的规则

Makefile里面是由一系列的规则组成的,这些规则格式如下:

target ... : prerequisites ... 
command

target 也就是一个目标文件,可以是 Object File,也可以是执行文件。还可以是一个标签(label)。

prerequisites就是生成target所需要的文件或是目标,也叫做依赖文件,就是冒号后面的那些 .c 文件和 .h 文件,每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件 main的依赖文件,依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。

command就是make需要执行的命令

这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于prerequisites 中的文件,其生成规则定义在 command 中。说白一点就是说,prerequisites中如果有一个以上的文件比 target 文件要新的话,command 所定义的命令就会被执行。这就是 Makefile 的规则。

示例:
		main: main.o test1.o test2.o .....
		gcc -o main	 main.o test1.o test2.o .....
		main.o:main.c
		gcc -c main.c
		.......

上述例子就是就一个简单的Makefile规则解释。


三、make是如何工作的

在默认的方式下,也就是我们只输入 make 命令。
1、make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“main”这个文件,并把这个文件作为最终的目标文件。
3、如果 main文件不存在,或是 main所依赖的后面的 .o 文件的文件修改时间要比 main这个文件新,那么,他就会执行后面所定义的命令来生成 main这个文件。
4、如果 main所依赖的.o 文件也存在,那么 make 会在当前文件中找目标为.o 文件的依赖性,如果找到则再根据那一个规则生成.o 文件。(这有点像一个堆栈的过程)
5、当然,你的 C 文件和 H 文件是存在的啦,于是 make 会生成 .o 文件,然后再用 .o 文件生成make 的终极任务,也就是执行文件 main 了。

这就是整个 make 的依赖性,make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make 就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理。make 只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

注意:如果我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,其中一个test1.c文件,那么根据我们的依赖性,我们的目标对应test1.o也会被重编译,由于test1.o修改时间比main要新,则就会重新链接。在一.makefile介绍中也有解释


四、makefile中使用变量

跟 C 语言一样 Makefile 也支持变量的,先看一下前面的例子:

main: main.o input.o calcu.o
 	gcc -o main main.o input.o calcu.o
 	......

上述例子中三个依赖文件中,我们输入了两遍,当工程复杂时,重复输入的工作就会非常费时间,而且非常容易输错,为了解决这个问题,Makefile 加入了变量支持。Makefile 中的变量都是字符串!类似 C 语言中的宏。

objects = main.o input.o calcu.o 
main: $(objects) 
	gcc -o main $(objects)

我们定义了一个变量objects,并且对这个变量进行了赋值,Makefile 中变量的引用方法在示例中已经体现。

赋值符 “=” “:=” “?=”

name:abc
curname = $(name)
name =efg 
print:
	curname: $(curname)

最终输出结果 curname :efg

name:abc
curname := $(name)
name =efg 
print:
	curname: $(curname)

最终输出结果 curname :abc

由此可以看出 “:=” 不会使用后面定义的变量,这就是 “=” “:=”两个的区别。

curname?=abc

上述代码的意思就是,如果变量 curname 前面没有被赋值,那么此变量就“abc”,
如果前面已经赋过值了,那么就使用前面赋的值。


五、makefile模式规则

 main: main.o input.o calcu.o 
 	gcc -o main main.o input.o calcu.o 
 main.o: main.c 
 	gcc -c main.c 
 input.o: input.c
 	gcc -c input.c  
 calcu.o: calcu.c 
 	gcc -c calcu.c  
 clean:
	rm *.o
	rm main

上述 Makefile 中将对应的.c 源文件编译为.o 文件,每一个 C 文件都要写一个对应的规则,如果工程中 C 文件很多的话显然不能这么做。为此,我们可以使用Makefile 中的模式规则,通过模式规则我们就可以使用一条规则来将所有的.c 文件编译为对应的.o 文件。

模式规则中,至少在规则的目标定定义中要包涵“%”,否则就是一般规则,目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的文件,类似与通配符,a.%.c 就表示以 a.开头,以.c 结束的所有文件。

objects = main.o input.o calcu.o
main: $(objects)
	gcc -o main $(object)

%.o : %.c
	#命令
clean:
	rm* .o
	rm main

其中修改后的示例中 命令 我们需要借助另一种强大的变量-自动化变量

Makefile 自动化变量

上面讲的模式规则中,目标和依赖都是一系列的文件,每一次对模式规则进行解析的时候都会是不同的目标和依赖文件,而命令只有一行,如何通过一行命令来从不同的依赖文件中生成对应的目标?自动化变量就是完成这个功能的!所谓自动化变量就是这种变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完,自动化变量只应该出现在规则的命令中,常用的自动化变量如表:

自动化变量描述
$@规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。
$%当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。
$<依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列的文件集合。
$?所有比目标新的依赖目标集合,以空格分开。
$^所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,“$^”会去除重复的依赖文件,值保留一份。
$+和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。
$*这个变量表示目标模式中"%"及其之前的部分,如果目标是 test/a.test.c,目标模式为 a.%.c,那么“$*”就是 test/a.test。
objects = main.o input.o calcu.o
main: $(objects)
	gcc -o main $(objects)
%.o : %.c
	gcc -c $<
clean:
	rm * .o
	rm main

六、Makefile伪目标

Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。使用伪目标主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的。

clean:
	rm *.o
	rm main

上述规则中并没有创建文件 clean 的命令,因此工作目录下永远都不会存在文件 clean,当我们输入“make clean”以后,后面的“rm *.o”和“rm main”总是会执行。可是如果我们“手贱”,在工作目录下创建一个名为“clean”的文件,那就不一样了,当执行“make clean”的时候,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的 rm 命令也就不会执行,我们预先设想的清理工程的功能也就无法完成。为了避免这个问题,我们可以将 clean 声明为伪目标,声明方式如下:

.PHONY : clean

声明 clean 为伪目标以后不管当前目录下是否存在名为“clean”的文件,输入“make clean”的话规则后面的 rm 命令都会执行。

总结

以上只是makefile的一些基础知识和基本语法,还有大量的makefile知识没有提到,本文也参照了《跟我一起写Makefile》,后续需要也可以根据此文档进行参考和学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值