目录
一、什么是makefile?
二、为什么要有makefile?
三、makefile的使用
1.依赖关系与依赖方法
2.伪目标
3.定义变量
4.特殊符号
四、makefile的执行逻辑
一、什么是makefile?
Makefile是一种自动化构建工具,make是一条指令,Makefile是一个文件,当我们创建名为Makefile的文件后在Makefile中按照一定的规则制定一些命令。然后我们在命令行输入make命令后会自动执行Makefile文件中的指令。
二、为什么要有makefile?
- 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
- makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
- make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Visual C++的nmake,Linux下GNU的make。
- make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
三、makefile的使用
首先在我们的项目下创建一个名为Makefile或名为makefile的文件,然后使用vim打开进行编辑。
1.依赖关系与依赖方法
假如我们有一个code.c文件,现在需要编译生成名为code的可执行文件,我们可以在Makefile中这样写:
code:code.c
gcc code.c -o code
依赖关系:就是所要生成的这个目标文件所依赖的文件是哪些。
依赖关系:就是所要生成的这个目标文件所依赖的方法是哪些。
注意:在依赖方法的前面需要加一个Tab制表符,使用4个空格不合法。
在Makefile写完以上指令后我们回到命令行,输入make命令则会有以下效果:
我们使用make指令后它会自动显示出gcc code.c -o code,如果不想让它显示可以在依赖方法前面加@如:
code:code.c
@gcc code.c -o code
2.伪目标
伪目标文件也就是说它并不会在自己的目录下真生成一个目标文件,它更像是一条指令,用了就没了但是可以多次使用,只要给目标文件用.PHONY修饰那么它就成为伪目标了,如下:
.PHONY:clean
clean:
rm -f code
因为生成clean这个目标不需要依赖任何文件所以就不用写。
在命令行执行.PHONY修饰的文件我们需要使用make+伪目标名,比如这里我们可以在命令行输入
make clean
3.定义变量
在某些时候我们为了方便后期修改通常会用变量来代替各种文件或指令,而在使用时需要用$符号加()可以理解为把它解包装,如把以上的命令全部改为变量的形式:
BIN=code
SRC=code.c
CC=gcc
FLAGS=-o
$(BIN):$(SRC)
$(CC) $(SRC) $(FLAGS) $(BIN)
4.特殊符号
%.o表示所有以.o为结尾的文件,同理%.c表示所有以.c结尾的文件。
$@:所有目标文件
$^:所有依赖的文件
$(shell ls *.c)或$(wildcard *.c): 在该目录里的所有以.c结尾的文件
$(SRC: .c = .o):SRC变量名表示的文件的.c变成.o生成新的文件名。
那么我们可以做出以下操作:
BIN=code
CC=gcc
#SRC=$(shell ls *.c)
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
LFLAGS=-o
FLAGS=-c
RM=rm -f
$(BIN):$(OBJ)
@$(CC) $(LFLAGS) $@ $^
@echo "linking ... $^ to $@"
%.o:%.c
@$(CC) $(FLAGS) $<
@echo "compling ... $< to $@"
.PHONY:clean
clean:
$(RM) $(OBJ) $(BIN)
注:
- 其中#表示屏蔽,这跟我们一般写代码用的屏蔽方式不一样
- 使用echo打出信息可以方便我们知道程序是否在正常执行。
- 不把源文件直接生成可执行文件而是先生成.o文件是因为后期对部分文件进行修改后可以单独编译再与原来编译好的文件一起做库链接,而不是全部文件重新编译一遍。 而如果直接把源文件生成可执行文件的话,到时候一个文件修改就需要全部文件重新编译一遍,效率非常的低。
四、makefile的执行逻辑
比如我们写这样一段指令:
上面的指令是能正常运行的,但我们发现在执行第一个语块的时候并没有code.o这个依赖文件,code.o需要在下一个语块中才能生成。其实makefile的执行逻辑是这样的,它在执行到一个文件不完整的语块的时候,会类似的先把它放在一个栈结构中然后往后去执行,直到后面的指令执行完需要它出栈的时候才执行,此时如果该语块依旧文件不完整则会报错。
一个目标文件被.PHONY修饰后变成伪目标是可以总是被执行的。而一个普通的目标文件只能被执行一次。这也是一种提高编译效率的方式,因为同样的内容已经生成了一份就没必要多次生成。
而当涉及的依赖文件的内容被修改时make指令才能再次被执行。但是Makefile如何知道某个文件被修改过呢?其实是通过文件的时间属性来判断的,我们可以通过stat+文件名来查看,如:
- Access时间:文件的最近访问时间。当文件内容被读取时,这个时间戳会更新。
- Modify时间:文件内容的最近修改时间。当文件的内容发生变化时,这个时间戳会更新。
- Change时间:文件属性的最近修改时间。这里的文件属性包括文件的权限、所有者、所属组等元数据。
可以巧记为“ACM”时间。makefile通过比较目标文件与依赖文件的Modify时间,如果依赖文件的Modify时间比目标文件的Modify时间要新,则可以通过make指令再次生成目标文件。
非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!