Linux修炼:自动化构建make/Makefile
Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!
我的博客:<但凡.
我的专栏:《编程之路》、《数据结构与算法之美》、《C++修炼之路》、《Linux修炼:终端之内 洞悉真理》
感谢你打开这篇博客!希望这篇博客能为你带来帮助,也欢迎一起交流探讨,共同成长。
目录
1、引入
2、基本概念
理解
3、推导
4、Makefile语法拓展
1、引入
在上一期我们介绍了gcc来编译源文件,可是大家在未来写代码的时候,有可能会有多个源文件需要我们去编译,那么我们怎么样才能像在vs那样,自动化的编译形成可执行程序呢?
我们可以通过这一期介绍的make/Makefile来实现自动化构建。
2、基本概念
make是Linux系统内置的命令,而Makefile是需要工程师自己建立的文件。
那么我们先来简单看一下怎么操作:
现在我们的目录中已经有了一个写好的code.c文件。
第一步新建一个名为Makefile的文件(或者makefile);
第二步,在文件中写入以下代码:
注意,第二行必须是table而不是空格!
第三步,输入命令 make,我们发现我们当前目录中生成了code文件:
第四步,我们直接执行这个可执行程序code:
接下来,我们在Makefile中写一下自动清理:
现在,我们执行make clean命令,就能够自动删除我们刚才实现的可执行程序了。
理解
现在我们来解释一下我们刚才都写了什么:
文件的第一行,code:code.c 是依赖关系;紧接着第二行叫依赖方法。依赖方法表明了依赖关系。注意第二行开头必须是TAB键,对于缩进有严格要求。依赖关系和依赖方法缺一不可。依赖关系中,冒号前面的我们叫目标文件,也就是我们生成的文件。冒号后面的叫依赖文件列表。这个列表可以有多个文件。
第三行.PHONY :clean声明 clean是一个伪目标,即它不代表实际要生成的文件名。第四行clean:
定义了一个名为 clean 的目标。当在命令行运行make clean时,Make 会执行该目标下的命令。伪目标总是被执行的。 什么叫总是被执行的呢?我们多次执行make,会发现他不让你编译了,因为他检测到你的源代码没有变化。这就叫总是不被执行,本质上是为了提升效率。如果我们多次执行clean,发现他总是会被执行。因为他被.PHONY修饰了。
那么系统是怎么知道我们的源文件有没有被更改呢?
我们看一下code.c的文件属性:
当我们的修改文件内容时,第一二个时间属性会更改,当访问时,后两个时间属性不会更改,但是第一个可能会更改,根据系统而定。当我们修改属性但是不修改内容时,只有第三个时间属性会改变。 系统根据我们code和code.c的Modify时间的新旧来判断是否需要重修编译。
所以说,系统判断的并不是你内容是否被更改了,只要你编辑了code.c,就算你去掉一个字母,又加上去了,内容没有改变,在系统眼里,code.c的Modify时间比code的时间新,那么code.c就允许再次被执行。
我们在执行make时,他是从上往下扫描的,默认都是实现遇到的第一个目标。所以说对于这两个目标的前后顺序是不能调换的。
3、推导
我们现在用一幅图来理解makefile是怎么推导的:
我们想要生成code就需要code.o,想生成code.o就需要code.s。以此类推。直到我们想生成code.i需要code.c,我们是有code.c的,所以就能生成了code.i,接着反向执行刚才的每个命令。其实系统在检测到我们某一个指令没有前置文件时,先把他入栈。只要没法生成就入栈,知道可以执行了,就依次出栈执行指令。
但是我们实际上不会这样写。往往我们习惯这样写:
code:code.o gcc code.o -o code
code.o:code.cgcc -c code.c -o code.o.PHONY:clean
#clean:rm -f code
4、Makefile语法拓展
接下来我们适度拓展一下makefile的语法:
在写makefile文件的时候,我们可以不直接把各种命令都写出来,而是以变量的形式来写。
BIN=code.exe
CC=gcc
SRC=code.c
OBJ=code.o
CFLAGS=-c
LFLAGS=-o
RM=rm -f$(BIN):$(OBJ)$(CC) $(LFLAGS) $(BIN) $(OBJ) #或者写成$(CC) $(LFLAGS) $@ $^
$(BOJ):$(SRC)$(CC) $(CFLAGS) $(SRC) #默认生成.o结尾的同名文件.PHONY:clean
clean:$(RM) $(BIN).PHONY:print
print:#打印并关闭回显#使用@关闭回显@echo $(BIN)@echo $(CC)@echo $(SRC)@echo $(FLAGS)@echo $(RM)
我们定义了一堆变量,我们的命令直接用变量代替就可以了,这类似于C语言中的宏或者全局变量,如果我们想换一些命令,比如把gcc换成g++,就直接切换这个变量就可以了。其中,$@代表上一行中冒号左边的内容,$^代表冒号右边所有内容。
我们可以把代码继续"升级":
BIN=code.exe
CC=gcc
SRC=$(whildcard *.c)
OBJ=$(SRC:.c=.o)
CFLAGS=-c
LFLAGS=-o
RM=rm -f$(BIN):$(OBJ)$(CC) $(LFLAGS) $(BIN) $(OBJ) #或者写成$(CC) $(LFLAGS) $@ $^
%.o:%.c$(CC) $(CFLAGS) $<.PHONY:clean
clean:$(RM) $(BIN).PHONY:print
print:#打印并关闭回显#使用@关闭回显@echo $(BIN)@echo $(CC)@echo $(SRC)@echo $(FLAGS)@echo $(RM)
whilecard是makefile内置的函数,可以实现查找当前目录下以.c为结尾的文件。当然我们也可以把.c改成.o或者其他的。第二行代码就是把SRC的所有文件中都换成同名的.o文件。%.o:%.c代表,冒号左侧有一百个文件,分别是code1.o,code2.o......,冒号右侧也有一百个文件,分别是code1.c,code2.c......,而$<代表着把以上的文件对应着,一个一个的交给.o文件。而&^代表把冒号右边的统一交给冒号左边的。因为最后我们只形成一个可执行程序。
当然了我们日常练习也没必要写的这么复杂。
好了,今天的内容就分享到这,我们下期再见!