makefile学习-2

变量

为什么要使用变量呢?看下面的makefile:

objs : main.o 

main.o : main.c 
           cc -c main.c

我们可以看到[.c]文件的字符串被重复了两次,如果我们的工程需要加入一个新的[.c]文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。

  • 定义变量
object:=Naruto.o Sakura.o Sasuke.o
CC = gcc
FLAG = -o

如果还想要给object追加

object+=Kakashi.o
  • 使用变量
helloworld:$(object)
    $(CC) $(object) $(FLAG) helloworld

什么括号都可以,但是要统一

  • 伪目标

1、如果使用 make clean命令,那么会直接运行 rm,无论当前目录下是否存在clean文件,输入命令"make clean"之后,"rm"命令都会被执行,而且,当一个目标被声明为伪目标后,make在执行此规则时不会试图去查找隐含规则来创建这个目标。这样也提高了make的执行效率,同时我们也不用担心由于目标和文件名重名。在书写伪目标规则时,首先需要声明目标是一个伪目标,之后才是伪目标的规则定义。

.PHONY:clean
clean:
    rm -rf $(object) *.i *.s

2、没有声明clean为伪目标,如果当前文件夹下没有clean文件,那么输入命令"make clean", rm -rf总会被执行,但如果当前工作目录存在文件"clean"时情况就不一样了,输入命令"make clean"时,编译器发现当前文件夹有一个clean文件,然后就错把它当成了目标文件,那么在编译器眼里它就是up-to-date,所以rm -rf也不执行了

clean:
    rm -rf $(object) *.i *.s

3、 目标和伪目标的区别就是一个能生成文件,一个不能生成文件

4、 就是那个.PHONY的声明,它其实就一个作用,把一个东西明明白白的告诉编译器,这是个伪目标,没有实体文件

5、这里要说明一点的是,clean不是一个文件,它只不过是一个动作名字,有点像C语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个lable的名字。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

6、通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。

7、.PHONY:之后的都是伪目标

通配符

% 任意一个

%.c 任意一个.c文件

* 所有

*.c 所有.c文件

? 匹配

hello:k?y.c
    gcc -o hello k?y.c

执行make时,编译器会根据当前工作目录进行匹配,比如当前文件夹有koy.c和key.c这两个文件,如果进行make,那么就会报错,编译器不知道选择哪个作为依赖文件,但如果当前文件夹只有key.c的话,那么编译器就会自动匹配了

自动变量

$@ 目标文件

$^ 依赖文件

$< 第一个依赖文件

hello:hello.c
    gcc -o $@ $^  
kakaxi:kakaxi.c
    gcc -o $@ $^

$@和$^会自动匹配目标文件和依赖文件

函数

  • SRC = $(wildcard *.c)

wildcard函数:获得当前文件夹内所有的.c文件

  • OBJS = $(patsubst %.c,%.o,$(SRC))

patsubst函数:将SRC变量里的所有.c文件转化为.o文件

make的工作方式

  • 读入所有的Makefile
  • 读入被include的其它Makefile
  • 初始化文件中的变量
  • 推导隐晦规则,并分析所有规则
  • 为所有的目标文件创建依赖关系链
  • 根据依赖关系,决定哪些目标要重新生成
  • 执行生成命令

Makefile书写规则

规则包含两个部分:

  • 依赖关系
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文件)
  • 生成目标的方法
  • 在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。make所完成的也就是这个目标。

TIPS

  • 显示规则

显式规则说明了如何生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令,通过依赖关系可以看出来。

  • 隐式规则
object:=helloone.o hellotwo.o hellothree.o  #object代表这三个文件
CC = gcc     
FLAG = -g     #参数
LIB =         #库文件
INCLUDE =     #包含文件
target = hello    #目标文件
$(target):$(object)
    $(CC) $(FLAG) $^ -o $@ $(LIB)    #$^:依赖文件   $@:目标文件
    
#伪目标
.PHONY:
clean:
    rm -rf *.o $(target)

主要靠的是make的自动推导,比如上述makefile,当前目录只有helloone.c hellotwo.c hellothree.c,并没有.o 文件,运行make命令时,由于本地文件夹内没有.o文件,编译器会自动将.c文件转为.o文件

由于make有自动推导的功能,所以隐式规则可以让我们比较粗糙地简略地书写Makefile,这是由make支持的。

  • 注释 : #

如果要在makefile里面使用#,使用"\#"

  • 如果要指定特定的Makefile,你可以使用make的“-f”和“--file”参数,如:make -f Make.Linux或make --file Make.AIX。
  • 在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原 模原样的放在当前文件的包含位置。include的语法是:

include<filename>filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和可以用一个或多个空格隔开。举个例子,你有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,那么,下面的语句:

include foo.make *.mk $(bar)

等价于:

include foo.make a.mk b.mk c.mk e.mk f.mk
  • make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找:

1.如果make执行时,有“-I”或“--include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。

2.如果目录/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。

  • 如果你的当前环境中定义了环境变量MAKEFILES,那么,make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,从这个环境变中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现错误,make也会不理。
  • 如果命令太长,你可以使用反斜框‘\’作为换行符
Last modification:June 10th, 2020 at 06:58 pm