当前位置:首页>Linux>OS13.【Linux】项目的自动化构建工具:make

OS13.【Linux】项目的自动化构建工具:make

  • 2026-10-11 06:12:31
OS13.【Linux】项目的自动化构建工具:make

目录

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的其他内容


1.情景引入: Linux下编译大型项目的一般方法

一般情况下,大型项目有很多源文件,其按类型、功能、模块分别放在若干个目录中,如何有序编译源文件?

Linux下编译大型项目可以用makefile命令,以开源的高性能键值存储数据库 redis 为例,目录的结构比较复杂

先将redis项目克隆到服务器上:

git clone https://github.com/redis/redis.git

tree命令查看redis目录的结构:

tree redis

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

使用切换到项目的目录后,用make命令编译项目:

cd redismake

全自动化构建

一段时间后完成编译

2.make和makefile

区别

make是命令,而makefile(或Makefile)是当前项目目录下的文件

make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“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

会发现仍然可以

演示makefile中的多个依赖关系

这里可将生成可执行文件的步骤进行拆解:预处理→编译→汇编→连接

注意:依赖关系写完后紧接着写依赖方法

依赖关系图:

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清除:

简单理解make的自动化推导

从上面的例子可以看出: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命令就是执行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不允许反复编译这样是为了提高运行效率

Linux下的三种时间类型

之前在OS6.【Linux】基本指令入门(5)[1]简单提到过

stat命令

其中一个是作用查看文件的时间(stat全称display file or file system status)

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

可以看到时间一共分为三种

三种时间类型

修改时间(Modification Time,mtime)

文件=文件内容+文件属性,更改文件内容会更新修改时间

改变时间(Change Time,ctime)

文件=文件内容+文件属性,更改文件属性会更新改变时间

删除或增加文件的内容modify和change会同时修改,因为文件大小属于文件属性

访问时间(Access Time,又称atime)

1.从定义上来讲,更改文件内容或者文件属性都会改变访问时间,但某些 Linux系统 会因为访问时间修改

太过于频繁而修改访问时间的更新策略: 例如访问多次才会更改更新访问时间

2.touch在新建文件时会更新所有时间

在不更改源文件的情况下让make反复编译

方法1:改变修改时间

见如下图:

方法2: .PHONY修饰

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就不会显示任何信息

3.makefile学习资料

参见《跟我一起写Makefile》作者: 陈皓

下载链接:https://scc.ustc.edu.cn/zlsc/czxt/200910/W020150417520333830657.pdf[2]

4.有关makefile的其他内容

1.makefile保存了编译器和链接器的参数选项,并且描述了所有源文件之间的关系

2.Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释

  • 显式规则说明了,如何生成一个或多个目标文件
  • make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写makefile,比如源文件与目标文件之间的时间关系判断之类
  • 在makefile中可以定义变量,当makefile被执行时,其中的变量都会被扩展到相应的引用位置上,通常使用 $(var) 表示引用变量
  • 文件指示。可以使用include关键字把别的Makefile包含进来,类似C语言中的include
  • 注释,makefile中可以使用 # 在行首表示行注释

参考资料

[1]

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

最新文章

随机文章