「Linux工具」自动化构建make/Makfile

引言:本篇介绍Linux中重要的自动化工具make,及其Makefile文件

本篇专栏:Linux专栏

作者主页:海盗猫鸥-CSDN

make/Makefile

自动化构建make/Makefile

基本理解和使用

简单的示例引入:

创建一个名为Makefile的普通文件

vim打开Makefile在里面输入一下代码

退出vim,执行make指令

可以看到我们写在Makefile文件中的指令,通过make指令执行了出来

由此我们可以先有一些简单的理解:

make就是一条指令,用来执行Makefile中的内容,而Makefile是一个文件

在上文中Makefile文件的示例代码中:

其意义为:code是我们的目标文件,其依赖于code.c这个文件,而依赖方法是下面的一条指令。当make执行这一段代码时,就会先去查找code.c,然后通过依赖方法的指令来构建出code文件

所以Makefile中每一组这样的代码都是一组依赖关系和一些依赖方法共同构成的

伪目标和文件时间属性对make的使用影响

清理示例引入伪目标

一个项目工程在实现的过程中,可能会产生一些中间文件,这些文件在总目标构建完成后,就需要被清理。我们先用简单的清理来做演示示例:

当我们这样定义好清理clean后,使用make clean,就可以自动清理掉code文件

通过上面的简单示例,我们提出并解决一下问题:

  1. 为什么执行make时只执行了构建code的指令,而clean则需要使用make clean来使用?

    这是因为make在扫描Makefile文件后,默认只会自动构建第一个目标文件,如果将clean放到最上面,那么make就会默认执行clean。

  2. clean:是什么?

    也是一组依赖关系,只不过clean这个目标不需要依赖于任何文件,所以:后面没有任何文件

  3. .PHONY:clean是什么?

    .PHONY就像我们c/cpp中的关键字一样,被其修饰的目标文件,在使用make调用时总是会被执行 对应的依赖关系和依赖方法,而为保证清理干净,使用.PHONY来修饰clean

文件时间属性理解执行

而要解释伪目标中提到的总是会被执行 ,就需要理解什么叫不被执行 ,由此我们观察以下现象:

我们可以发现,第一次执行make创建出code后,再make就不再会执行创建code的指令,这是什么呢?

从一个项目的视角来看,当我们只修改了部分文件,那么我们也只需要将修改的文件进行重新编译即可,而没有修改的文件不需要再执行编译过程,这样只会浪费时间。

也就是说,code依赖于code.c,而++code.c源码我们并没有修改++ ,所以在我们第二次想要使用make来再编译code.c时,++make就会拒绝执行++ ,这样就可以++防止无意义的编译过程++。

而我们又提到,++.PHONY++ ++修饰的目标文件总是会被执行++ ,所以我们此时使用.PHONY来修饰code,就可以消除掉上面的情况

++但上文中的对未更改的文件,默认不执行重编译是一种正确的做法,我们此处仅仅是借这个现象引出问题。++

而++make又是怎么判断code.c没有被修改,而进一步判断出此时不重新执行构建code文件的操作的呢++ ?这就和文件的时间属性有关了:

我们知道文件的时间属性有三个,使用stat命令查看code.c的时间属性

  1. 访问时间Access(Access Time(atime)),代表文件内容最后一次被访问的时间,在读取文件内容时就会更新。

    例如使用 morecat 等命令查看文件内容时,这个时间就会更新。不过,lsstat 命令查看文件信息并不会修改文件的访问时间。

  2. 修改时间Modify (Modifcation Time(mtime)),代表文件内容最后一次被修改的时间,当文件内容被更改时更新。

  3. 变更时间Change(Change Time(ctime)),文件状态/属性最后一次被修改的时间,修改文件权限、所有者等文件属性时会被修改。

mtimectime大多数时候会同步改变(修改文件内容就会影响文件大小,而文件大小也是文件属性的一种)。

所以,当文件被修改时,其修改时间属性会变动,++make直接根据code.c源文件的修改时间,和code的修改时间进行对比++ ,如果code.c的时间比code的时间更新,就说明code.c源文件在生成code之后又修改过,make就会再执行依赖关系来重编译code,若code.c的文件修改时间比code更旧,说明没有被修改,就不需要再执行重编译。

而我们之前学过的touch指令,实际就是用来修改文件时间的,会同时更新三个时间属性,我们就可以借此来验证上面的说法:

make/Makefile推导规则

我们展开code的编译步骤观察:

可以发现,我们执行make后想要创建code文件时,虽然code.o不直接存在,但很显然make会在Makefile中查找code.o的依赖关系,并试图通过其依赖方法来先构建出code.o文件,以此类推.

make最终将从code.ccode的所有过程都执行了一遍。且顺序和依赖定义顺序相反:

所以我们可以推导make/Makefile的执行过程就类似于入栈出栈的过程。

  1. 尝试构建code,但code.o不存在,将code的依赖方法入栈
  2. Makefile中查找能构建出code.o的依赖关系,但构建code.o的依赖文件code.s依然不存在,将code.o的依赖方法入栈...以此类推
  3. 直到构建code.i的依赖关系中,code.c文件已经存在,执行该次构建,形成code.i
  4. 依次取出栈顶的依赖方法,最后构建出code

make工作方式总结

语法扩展

仅仅是上面的一些内容,体现不出make/Makefile自动化构建的优势,此时引入更多内容,然后初步构建一个自动化编译的Makefile文件。

第一版,使用示例来引入语法概念:

++解释:++

  1. Makefile中可以定义变量,变量名=指代内容
  2. $(变量):可以使用变量中的内容来进行替换
  3. $@:代表目标文件名
  4. $^:代表依赖文件列表

所以其实变量的使用很类似于C/CPP中对宏定义和宏替换的使用

但上述的一个版本实际上的效果和之前的例子大差不差,因为也还是只不过一个文件,只是把使用变量来进行了内容替换,还是不足以体现Makefile的便捷,我们简单模拟一个有许多源文件的大项目,使用Makefile来一次性全部编译:

使用shell脚本创建一百个源文件count=1; while [ $count -le 100 ]; do touch code${count}.c; let count++; done


++解释:++

Makefile 复制代码
BIN=myproj.exe    	 #最终目标文件名
CC=gcc    			 #使用编译器
#SRC=$(shell ls *.c)    
SRC=$(wildcard *.c)  #使用wildcard获取到所有的.c文件名
OBJ=$(SRC:.c=.o)     #通过将SRC中所有文件名.c替换为.o来获取所有.o文件名
LFLAGS=-o   		 #链接指令选现
FLAGS=-c    		 #汇编形成.o文件的指令选现
RM=rm -f    
    
$(BIN):$(OBJ)      	 				#依赖关系myproj.exe:code1.o code2.o ...... code100.o
  @$(CC) $(LFLAGS) $@ $^    		#依赖方法@gcc -o myproj.exe code1.o ... code100.o
  @echo "linking ... $^ to $@"      #打印链接信息                                                                                     
%.o:%.c  			 				#依赖关系 (同名).o:(同名).c  
  @$(CC) $(FLAGS) $<     			#依赖方法@gcc -c (同名).c
  @echo "compling ... $< to $@"    
.PHONY:clean    
clean:    
  $(RM) $(OBJ) $(BIN)  				#rm -f *.o myproj.exe

补充

  1. @:使用在依赖关系前,可以让依赖关系不再命令行窗口中显示出来(默认是会显示的)
  2. %Makefile中的一种通配符
  3. $<展开所有的依赖文件,并一个一个的放入依赖关系中并执行

执行:


由此可见,make/Makefile在大工程中的使用价值~

本篇对于make/Makefile的介绍和使用示范就到这里,我们下期再见~

相关推荐
星晨雪海2 小时前
MyBatis-Plus 常用 CRUD 方法大全
linux·tomcat·mybatis
2739920292 小时前
Ubuntu 文件系统修复指南
linux·ubuntu·fsck
云栖梦泽2 小时前
Linux内核与驱动:2.驱动基础(编译驱动)
linux·服务器·c++
Mariooooooooooo3 小时前
个人5070离线安装nvidia显卡驱动
linux
龙泉寺天下行走3 小时前
记一次windows SSH无法免密登录Linux的处理
linux·运维·ssh
kainx3 小时前
华为RH1288 V2服务器风扇异常狂转iBMC的管理网口无法连上查看硬件告警-通过ESXi启用shell安装ipmitool修改iBMC网络配置
linux·运维·服务器·网络·esxi·vmware
i建模3 小时前
Ubuntu 中使用 LVM(逻辑卷管理)挂载磁盘
linux·运维·ubuntu
cyber_两只龙宝3 小时前
【Docker】Dockerfile构建镜像实验全流程详解
linux·运维·docker·云原生
de_wizard3 小时前
Linux 下安装 Golang环境
linux·运维·golang