makefile使用总结--规则

发布时间 2023-04-03 22:39:27作者: 是对的人

本文参考《跟我一起写 Makefile》编写,并做了一些适合个人习惯的修改,稍加总结而成。

 

一篇文章肯定不够详细记录makefile所有的知识,所以这篇接着描述Makefile的规则。

规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。

一、语法

targets : prerequisites
    command
    ...
或是这样:
targets : prerequisites ; command
    command
    ...
  • targets 是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。
  • command 是命令行,如果其不与“target:prerequisites”在一行,那么,必须以[Tab键]开头。如果命令太长,你可以使用反斜框(‘\’)作为换行符。
  • prerequisites 是目标所依赖的文件(或依赖目标)。如果其中的某个文件要比目标文件要新,需要重生成目标文件。

规则告诉make两件事,文件的依赖关系和如何成成目标文件。一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。

二、举例

foo.o : foo.c defs.h
    cc -c -g foo.c

foo.o是目标文件,foo.c和defs.h是目标所依赖的源文件,和一个命令“cc -c -g foo.c”(以Tab键开头)。这个规则告诉我们两件事:

  1. 文件的依赖关系:foo.o依赖于foo.c和defs.h的文件,如果foo.c和defs.h的文件日期要比foo.o文件日期要新,或是foo.o不存在,那么依赖关系发生。
  2. 如果生成(或更新)foo.o文件。就需要执行cc命令,其说明了如何生成foo.o这个文件。(当然foo.c文件include了defs.h文件)

 三、规则中使用通配符

如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三各通配符:“*”,“?”和“[...]”。通配符代替了一系列的文件,如“*.c”表示所以后缀为c的文件。

print: *.c
    lpr -p $?
    touch print

上面这个例子说明了目标print依赖于所有的[.c]文件。

 四、伪目标

clean:
    -rm *.o

在工程编译过程中,会生成许多文件编译文件。上面这个例子提供了一个清除它们的“目标”以备完整地重编译而用。 (以“make clean”来使用该目标)

这里要说明一点的是,clean不是一个文件,它只不过是一个动作名字,有点像C语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后指出这个lable的名字。

为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。

.PHONY : clean
clean :
    -rm edit *.o

只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。

当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。

这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用“伪目标”这个特性:

all : prog1 prog2 prog3
    .PHONY : all
prog1 : prog1.o utils.o
    cc -o prog1 prog1.o utils.o

prog2 : prog2.o
    cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
    cc -o prog3 prog3.o sort.o utils.o

Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三个目标就总是比“all”这个目标新。所以,其它三个目标的规则总是会被执行。

 随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也可成为依赖。看下面的例子:

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
    rm program
cleanobj :
    rm *.o
cleandiff :
    rm *.diff

“make clean”将清除所有要被清除的文件。“cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的。