make和Makefile中的规则和变量

时间:2021-02-17 12:52:40

make和Makefile中的规则和变量



make机制的运行环境需要一个命令行程序make和一个文本文件Makefile

make命令执行后有3个退出码:

  • 表示成功执行。

  • 如果make运行时出现任何错误,则返回1.

  • 如果使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2.


make的使用格式是:

make [options] [target] ...

optionsmake工具的参数选项,targetmakefile中指定的目标。

make工具的参数选项

选项

含义

-f filename

显式地指定文件作为Makefile

-C dirname

指定make在开始运行后的工作目录为dirname

-e

不允许在Makefile中替换环境变量的赋值

-k

执行命令出错时,放弃当前目标,继续维护其他目标

-n

按实际运行时的执行顺序模拟执行命令(包括用@开头的命令),没有实际执行效果,仅仅用于显示执行过程

-p

显示makefile中所有的变量和内部规则

-r

忽略内部规则

-s

执行但不显示命令

-S

如果执行命令出错就退出

-t

修改每个目标文件的创建日期

-I

忽略运行make中执行命令的错误

-V

显示make的版本号

make操作管理Makefile文件的规则

  • 如果这个工程没有编译过,那么所有C文件都需要被编译和链接

  • 如果这个工程中的某几个C文件被修改,则只需编译被修改过的C文件,并连接目标程序

  • 如果这个工程的头文件被修改了,则需要编译引用了这几个头文件的C文件,并链接目标程序

makefile中,目标名称的指定通常有以下惯例:

  • all:表示编译所有的内容,时执行make时默认的最终的目标。

  • clean:表示清除所有目标文件。

  • distclean:表示清除所有的内容。

  • install:表示进行安装的内容。

Makefile的书写规则

Makefile的书写规则包含两个部分,一个是依赖关系,一个是生成目标的方法。

Makefile中只有一个最终目标,第一条规则中的第一个目标将被确立为最终的目标。

Makefile里主要包含了5方面的内容:显式规则、隐式规则、变量定义、文件指示和注释。

  • 显式规则:显式规则说明了如何生成一个或多个目标。这需要由Makefile的书写者显式指出要生成的文件、文件的依赖文件及生成的命令。

  • 隐式规则:由于make有自动推导的功能,会选择一套默认的方法进行make,所以隐式的规则可以让开发者比较简略地书写Makefile,这是由make所支持的。

  • 变量定义:在Makefile中需要定义一系列的变量,一般都是字符串,它类似C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

  • 文件指示:包括三部分,第一部分是在一个Makefile中引用另一个Makefile,就像C语言中的include一样包含进来;第二部分是根据某些情况指定Makefile中的有效部分,就像C语言中的预编译宏#ifdef;第三部分就是定义一个多行的命令。

  • 注释:Makefile中只有行注释,和UNIXshell脚本一样,其注释符使用“#”字符,这就像C/C++中的双斜杠“//”一样。如果需要在Makefile中使用“#”字符,可以用反斜杠进行转义,如“\\#”

  • Makefile文件中的命令必须要以“Tab”键开始。

Makefile的基本语法规则

Makefile的语法格式如下:

targets : prerequisites

command

...

或者是:

targets : prerequisites ; command

command

...

  • targets是目标文件名,多个文件以空格分开,可以使用通配符。一般说来,Makefile的目标是一个文件,但也有可能是多个文件。

  • prerequisites是目标所依赖的文件(或依赖目标)。

  • command是命令行,如果它不与”targets:prerequisites”在一行,那么必须以Tab键开头,如果和prerequisites在同一行,那么可以用分号作为分隔。如果命令太长,可以使用反斜杠“\”作为换行符。make对一行中有多少个字符没有限制。

对于Makefile文件:

all : main.c foo1.c foo2.c foo3.c

gcc main.c foo1.c foo2.c foo3.c -o all

allMakefile时最终目标,main.c,foo1.c,foo2.c,foo3.c是目标所依赖的源文件,而只有一个命令“gcc main.c foo1.c foo2.c foo3.c -o all”是生成目标的方法:

  1. 文件的依赖的关系:可执行文件all依赖于main.c,foo1.c,foo2.c,foo3.c文件,如果main.c,foo1.c,foo2.c,foo3.c中的任何一个的修改日期比all文件日期新,或者是all不存在,那么依赖关系发生。

  2. 如何生成(或更新)all文件。那个gcc命令,说明了all这个文件生成目标的方法。

在规则中使用通配符

make支持3种通配符:“*”,“?”,“[...]”

~”字符在文件名中有特殊用途:比如“~/test”表示当前用户的$HOME目录下的test目录;“~zhangfan/test”则表示用户zhangfan的宿主目录下的test目录。

通配符代替了一系列的文件,如”*.c”表示了所有后缀名为.c的文件。如果文件名中含有通配符,如“*”,那么可以用转义字符斜杠”\”,如“\*”来表示真实的”*“字符,而不是任意长度的字符串。


可以使用一个特殊的标记“.PHONY”来显式地指明一个目标是伪目标,向make说明,不管是否有这个文件,这个目标都是伪目标:

.PHONY : clean

只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只要在命令提示符下输入命令“make clean”即可。于是整个过程可以这样写,例如:

.PHONY : clean

clean :

rm all main.o kbd.o command.o \

insert.o search.o files.o

自动生成依赖关系

可以使用GNUC/C++编译器的“-MM”参数选项,使其自动寻找源文件中包含的头文件,并生成一个依赖关系。例如main.c文件中有defs.h头文件,那么:

gcc -MM main.c

其输出是:

main.o : main.c defs.h


变量

变量在声明时需要给予初值,而在使用时,需要在变量名前加上“$”符号,但最好用小括号“()”或是花括号”{}”把变量给引用起来。如果使用真实的”$”字符,那么需要用”$$”来表示。

下面Makefile等同:

objects=program.o foo.o utils.o

program:$(objects)

cc -o program $(objects)

$(objects):defs.h

等同于:

objects=program.o foo.o utils.o

program:program.o foo.o utils.o

cc -o program program.o foo.o utils.o

program.o foo.o utils.o:defs.h


赋值变量

1、使用“:=”操作符,使得前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。

2、使用”?=”操作符:例如foo ?= bar 如果foo没有被定义过,那么变量foo的值就是“bar”,否则此语句什么也不做,这段代码等同于:

ifeq($(origin foo),undefined)

foo=bar;

endif

3、使用“+=”操作符,可以追加值,

objects=main.o foo.o bar.o

objects+=another.o

等同于

objects=main.o foo.o bar.o

objects:=$(objects) another.o


define关键字

使用define定义的命令变量中没有以“Tab”键开头。

define two-lines

echo foo

echo $(bar)

endef

这段代码将变量two-lines定义为两条命令echo fooecho $(bar)


override指示符

有的变量时通过make的命令行参数进行设置的,那么Makefile将忽略这个变量的赋值。如果想在Makefile中设置这类参数的值,那么可以使用“override”指示符。

override =

override :=

override +=

define关键字中也同样可以使用override指示符:

override define foo

bar

endef


目标变量和模式变量

1、目标变量

:

:override

为目标序列,可以是各种赋值表达式,如”=”、”:=“,”+=“,”?=”。第二个语法针对于make命令行参数带入的变量,或是系统环境变量。

prog : CFLAGS=-g

prog : prog.o foo.o bar.o

$(CC) $(CFLAGS) prog.o foo.o bar.o

prog.o : prog.c

$(CC) $(CFLAGS) prog.c

foo.o : foo.c

$(CC) $(CFLAGS) foo.c

bar.o : bar.c

$(CC) $(CFLAGS) bar.c

等同于

prog : prog.c foo.c bar.o

gcc -g prog.o foo.o bar.o

prog.o : prog.c

gcc -g prog.c

foo.o : foo.c

gcc -g foo.c

bar.o : bar.c

gcc -g bar.c

2、模式变量

可以给定一种“模式”,把变量定义在符合这个模式的所有目标中。

:

:override

是模式序列,可以是多种模式,override同样是针对系统环境传入的变量或是make命令行指定的变量。

make的模式变量一般是至少含有一个“%”的,所以,可以以如下方式给所有以”.o”结尾的目标定义目标模式变量:

%.o : CFLAGS=-o