实战Makefile前,该知道那些知识?

2022-05-10 08:42:13 浏览数 (1)

在前一篇文章讲解了Makefile的一些概念和原理,接下来说说Makefile的一些知识点。

make与make clean

生成目标文件规则(make命令):

执行make命令则会根据当前目录的Makefile文件定义的规则生成对应的目标文件。

如果Makefile为其他名字,比如makefile.linux,则需要使用make的参数(-f or --file)执行对应的Makefile文件,例如:

代码语言:javascript复制
make -f makefile.linux

清空目标文件的规则(make clean命令):

每个Makefile中都应该写一个清空目标文件( .o 和目标文件等)的规则,这不仅便于重编译,也很 利于保持文件的清洁。也许在写Makefile的时候,都要养成这样一个习惯,一般的风格都是:

代码语言:javascript复制
clean:
    rm $(obj) *.o

更为稳健的做法是(原因:如果当前目录存在clean文件,该命令会执行失败),解决办法:增加伪目标:.PHONY:clean:

代码语言:javascript复制
.PHONY:clean
clean:
    rm $(obj) *.o

注意:

  • clean的规则不要放在文件 的开头,不然,会变成make的默认目标,一般:clean从来都是放 在文件的最后。

书写规则

显示规则(@字符):

当用@字符在命令前面时,那么执行这条命令的时候,这条命令不会显示出来。对比: 命令前加@字符例子:

代码语言:javascript复制
rice@rice:~/rice_file/mkfile$ cat Makefile 
exec:
  @echo "rice makefile"
rice@rice:~/rice_file/mkfile$ make
rice makefile

命令前不加@字符例子:

代码语言:javascript复制
rice@rice:~/rice_file/mkfile$ cat Makefile 
exec:
  echo "rice makefile"
rice@rice:~/rice_file/mkfile$ make
echo "rice makefile"
rice makefile

注意:

make命令参数 -s--silent--quiet 则是全面禁止命令的显示

命令执行规则:

当依赖目标新于目标时,make会一条一条的执行其后的命令。其中,加入要让上一条命令的结果应用在下一条命令时,应使用分号分隔这两条命令,并且不能将两条命令写在同一行。

例子1:

Makefile:

代码语言:javascript复制
exec:
    @cd /home/rice
    @pwd

结果:

代码语言:javascript复制
/home/rice/rice_file/mkfile

例子2:

Makefile:

代码语言:javascript复制
exec:
    @cd /home/rice;pwd

结果:

代码语言:javascript复制
/home/rice

从上面两个例子可以证明命令依赖的规则。

命令出错规则(-符号):

当命令运行完,make会检测每个命令的返回码,如果返回成功,那make会执行下一条命令,当规所有的命令成功返回后,make执行完成。如果一个规则中的某个命令出错了(命令退出码 非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。

有时命令的出错并不表示错误。例如mkdir命令,建立一个目录,如果目录不存 在,则mkdir不会出现错误。如果目录已存在,那么将产生错误。从例子说明,mkdir的出错并没有对其他命令产生影响,因为我只要目录存,所以mkdir出错不应该终止命令规则的运行。

为了解决上述问题,只需要在Makefile的命令行前加一个符号-,即使命令执行出错,也依然继续执行后续的命令。

代码语言:javascript复制
.PHONY:clean
clean:
    -rm $(obj) *.o

变量

变量的定义

Makefile也支持变量定义,变量的定义也让的我们的Makefile更加简化,可复用。

变量的定义:一般采用大写字母,赋值方式像C语言的赋值方式一样。

代码语言:javascript复制
DIR = ./src/

变量取值:使用括号将变量括起来再加美元符。

代码语言:javascript复制
FOO = $(DIR)

Makefile除了使用'='进行赋值,还有其他赋值方式,比如':='和'?=',接下来我们来对比一下这几种的区别:

赋值符'=':

代码语言:javascript复制
PARA = RICE
CURPARA = $(PARA)
PARA = rice

print:
  @echo $(CURPATA)

结果为:

代码语言:javascript复制
rice

变量CURPARA的值并不是“RICE”。其值为PARA最后一次赋值的值。说明,赋值符“=”,可以借助另外一个变量,可以将变量的真实值推到后面去定义。也就是变量的真实值取决于它所引用的变量的最后一次有效值。

赋值符':=':

代码语言:javascript复制
PARA = RICE
CURPARA := $(PARA)
PARA = rice

print:
  @echo $(CURPATA)

结果为:

代码语言:javascript复制
RICE

变量CURPARA的值为“RICE”。“=”和“:=”的区别就在这里,“:=”只取第一次被赋值的值。

赋值符'?=':

代码语言:javascript复制
PARA = RICE
PARA ?= rice

print:
  @echo $(PATA)

结果为:

代码语言:javascript复制
RICE

如果上面例子第一行去掉,结果为:

代码语言:javascript复制
rice

说明,如果变量PARA 前面没有被赋值,那么此变量就是“rice”,如果前面已经赋值过,那么就使用前面赋的值。

赋值符' =':

Makefile中的变量是字符串,有时候我们需要给前面已经定义好的变量添加一些字符串进去,此时就要使用到符号" =":

代码语言:javascript复制
OBJ = main1.o main2.o
OBJ  = main3.o

OBJ的值为:”main1.o,main2.o,main3.o“。说明“ =”用作与变量的追加。

系统自带变量:

系统自定义了一些变量,通常都是大学,比如CC,PWD,CLFAG等等,有些有默认值,有些没有,比如以下几种,如下

  • CPPFLAGS:预处理器需要的选项,如:-l
  • CFLAGS:编译的时候使用的参数-Wall -g -c
  • LDFLAGS:链接库使用的选项-L -l

其中:默认值可以被修改,比如CC默认值是cc,但可以修改为gcc:CC=gcc

自动变量:

Makefile的语法提供一些自动变量,这些变量可以让我们更加快速的完成Makefile的编写,其中自动变量只能在规则中的命令使用,常用的自动变量如下:

  • $@:规则中的目标
  • $<:规则中的第一个依赖文件
  • $^:规则中的所有依赖文件
代码语言:javascript复制
CC = gcc
OBJ = main.o add.o
output: $(OBJ)
  $(CC) -o $@ $^
main.o: main.c
  $(CC) -c $<
add.o: add.c
  $(CC) -c $<

.PHONY:clean
clean:
  @rm $(OBJ) output

模式规则

模式规则实在目标及依赖中使用%来匹配对应的文件,我们依旧使用上面的例子,采用模式规则格式,如下:

代码语言:javascript复制
CC = gcc
OBJ = main.o add.o
output: $(OBJ)
  $(CC) -o $@ $^
%.o: %.c
  $(CC) -c $<

.PHONY:clean
clean:
  @rm $(OBJ) output

其中:

main.o由main.c生成

add.o 由add.c生成

函数

Makefile提供了大量的函数,其中我们经常使用的函数主要有两个(wildcard,patsubst)。

wildcard函数:用于查找指定目录下指定类型的文件,函数参数:目录 文件类型,使用方法:

代码语言:javascript复制
SRC = $(wildcard ./src/*.c)

print:
  @echo $(SRC)

结果:

代码语言:javascript复制
./src/add.c ./src/main.c

表示:找到目录./src下所有后缀为.c的文件,并赋值给变量SRC。命令执行完,SRC变量的值:./src/add.c ./src/main.c

patsubst函数:用于匹配替换。函数参数:原模式 目标模式 文件列表,使用方法:

代码语言:javascript复制
SRC = $(wildcard ./src/*.c)
OBJ = $(patsubst %.c, %.o, $(SRC))

print:
  @echo $(OBJ)

结果:

代码语言:javascript复制
./src/add.o ./src/main.o

表示:把变量中所有后缀为.c的文件替换为.o。命令执行完,OBJ变量的值:./src/add.o ./src/main.o

0 人点赞