目录
1.情景引入: Linux下编译大型项目的一般方法
2.make和makefile
区别
makefile的简单编写
依赖关系和依赖方法
清理方法
演示makefile中的多个依赖关系
简单理解make的自动化推导
make的默认选项
判断可执行文件是否最新的方法
Linux下的三种时间类型
stat命令
三种时间类型
修改时间(Modification Time,mtime)
改变时间(Change Time,ctime)
访问时间(Access Time,又称atime)
在不更改源文件的情况下让make反复编译
方法1:改变修改时间
方法2: .PHONY修饰
伪目标的作用
简写符号
$@和%^
@
3.makefile学习资料
4.有关makefile的其他内容
一般情况下,大型项目有很多源文件,其按类型、功能、模块分别放在若干个目录中,如何有序编译源文件?
Linux下编译大型项目可以用makefile命令,以开源的高性能键值存储数据库 redis 为例,目录的结构比较复杂
先将redis项目克隆到服务器上:
git clone https://github.com/redis/redis.git
tree命令查看redis目录的结构:
tree redis


而编译这些源文件只需要一个命令:make,而项目的构建方法写在Makefile文件中:

使用切换到项目的目录后,用make命令编译项目:
cd redismake
全自动化构建

一段时间后完成编译

make是命令,而makefile(或Makefile)是当前项目目录下的文件
make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,,找到了就解释这个文件: 根据规则调用编译器、汇编器、链接器产生最后的输出

例如有一个test.c文件(功能是打印Hello World)需要编译,可以这样写makefile:
test:test.c gcc test.c -o test #必须按tab键来分隔,否则会提示missing separator
make命令执行后,成功生成test可执行文件,并且./test命令让test打印了Hello World!字符串:

上述的makefile文件的"test:test.c"为依赖关系,表示test可执行文件依赖于test.c文件
依赖关系必须有依赖方法," gcc test.c -o test"为依赖方法,必须按tab键来开头
生成test可执行文件后,再次使用make会发现无法再次编译test.c,make命令会提示"up to date",即最新的

一般情况下需要清理掉test可执行文件后才能重新make,这里不建议手动执行rm命令,出错风险较大,建议将rm****写到makefile中
test:test.c gcc test.c -o testclean:#clean没有依赖关系,:的右侧不用写 rm -rf test
使用make clean对可执行文件进行清除:
make clean
之后就能继续编译test.c

会发现执行make clean时,make会找makefile中的"clean"字符串,之后执行,现将clean改成step,看看执行make step能否清除可执行文件
test:test.c gcc test.c -o teststep: rm -rf test
会发现仍然可以

这里可将生成可执行文件的步骤进行拆解:预处理→编译→汇编→连接
注意:依赖关系写完后紧接着写依赖方法
依赖关系图:

test:test.o
gcc test.o -o test
test.o:test.s
gcc -c test.s -o test.o
test.s:test.i
gcc -S test.i -o test.s
test.i:test.c
gcc -E test.c -o test.i
clean:
#其实可以写成rm -rf test test.o test.s test.i
rm -rf test
rm -rf test.o
rm -rf test.s
rm -rf test.i
make命令执行后,会产生test.i、test.o、test.s这些中间文件

可以使用make clean清除:

从上面的例子可以看出:test依赖于test.o,但没有后者,发现test.o依赖于test.s, 但没有后者, test.s依
赖于test.i, 但没有后者, 发现test.i依赖于test.c, test.c是有的,所以从gcc -E test.c -o test.i命令开始执
行
类似”递归”,像栈式结构
如果打乱顺序,make也能执行,例如
test.o:test.s
gcc -c test.s -o test.o
test:test.o
gcc test.o -o test
test.i:test.c
gcc -E test.c -o test.i
test.s:test.i
gcc -S test.i -o test.s
clean:
rm -rf test
rm -rf test.o
rm -rf test.s
rm -rf test.i
照样能按正确的顺序执行:

单独的一个make命令就是执行make的默认选项
生成可执行文件后,调换clean到第一行
clean:
rm -rf test
rm -rf test.o
rm -rf test.s
rm -rf test.i
test.o:test.s
gcc -c test.s -o test.o
test:test.o
gcc test.o -o test
test.i:test.c
gcc -E test.c -o test.i
test.s:test.i
gcc -S test.i -o test.s
执行make命令后:

结论:make会自顶向下扫描makefile,遇到的第一个选项就是make的默认选项
上文提到了生成test可执行文件后,再次使用make会发现无法再次编译test.c,make命令会提示"up to date",即最新的,那么make是如何判断可执行文件是否最新的呢?
答:**比较修改时间,**可执行文件相对于源文件是老的,不需要重新编译;可执行文件相对于源文件是新的,可以重新编译
当修改test.c后就能再次执行make
结论:如果源文件文件没有更新过,make不允许反复编译这样是为了提高运行效率
之前在OS6.【Linux】基本指令入门(5)[1]简单提到过
其中一个是作用查看文件的时间(stat全称display file or file system status)

例如查看之前生成的可执行文件test

可以看到时间一共分为三种
文件=文件内容+文件属性,更改文件内容会更新修改时间
文件=文件内容+文件属性,更改文件属性会更新改变时间
删除或增加文件的内容modify和change会同时修改,因为文件大小属于文件属性
1.从定义上来讲,更改文件内容或者文件属性都会改变访问时间,但某些 Linux系统 会因为访问时间修改
太过于频繁而修改访问时间的更新策略: 例如访问多次才会更改更新访问时间
2.touch在新建文件时会更新所有时间
见如下图:

phony adj.假的
.PHONY修饰的作用:依赖方法总是被执行
使用 .PHONY 来声明伪目标,伪目标表示目标名并不代表真正的文件名,与实际存在的同名文件没有相互关系,因此伪目标不管同名目标文件是否存在都会执行对应的生成指令
例如以下写法:
.PHONY:test
test:test.c
gcc test.c -o test
clean:
rm -rf test
认为test是伪目标,每次make都会执行gcc test.c -o test,而不管test是否更新过

如果test没有用.PHONY修饰,那就要看test的修改时间
但工程上不建议使用.PHONY修饰来可执行文件,建议用.PHONY修饰clean,使清理删除临时文件或目标文件的工作每次都被执行,以便达到可以进行重新编译的效果,例如:
test:test.c
gcc test.c -o test
.PHONY:clean
clean:
rm -rf test
这样就能多次clean了:

1. 使目标对象无论如何都要重新生成
2. 并不生成目标文件,而是为了执行一些指令
可以使用$@ %^来简写
test:test.c
gcc $^ -o $@
.PHONY:clean
clean:
rm -rf test
其中**@代表:前面的∗∗test,而∗∗^代表:后面的**test.c
仍然能正常make

使用@可以隐藏命令的执行细节
test:test.o
@gcc test.o -o test
test.o:test.s
@gcc -c test.s -o test.o
test.s:test.i
@gcc -S test.i -o test.s
test.i:test.c
@gcc -E test.c -o test.i
.PHONY:clean
clean:
rm -rf test test.o test.s test.i
这样使用make就不会显示任何信息

参见《跟我一起写Makefile》作者: 陈皓
下载链接:https://scc.ustc.edu.cn/zlsc/czxt/200910/W020150417520333830657.pdf[2]
1.makefile保存了编译器和链接器的参数选项,并且描述了所有源文件之间的关系
2.Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释
显式规则说明了,如何生成一个或多个目标文件 make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写makefile,比如源文件与目标文件之间的时间关系判断之类 在makefile中可以定义变量,当makefile被执行时,其中的变量都会被扩展到相应的引用位置上,通常使用 $(var) 表示引用变量 文件指示。可以使用include关键字把别的Makefile包含进来,类似C语言中的include 注释,makefile中可以使用 # 在行首表示行注释
OS6.【Linux】基本指令入门(5): https://zhangcoder.blog.csdn.net/article/details/145813382?spm=1011.2415.3001.5331
[2]https://scc.ustc.edu.cn/zlsc/czxt/200910/W020150417520333830657.pdf: https://scc.ustc.edu.cn/zlsc/czxt/200910/W020150417520333830657.pdf