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

发布于:2025-06-28 ⋅ 阅读:(15) ⋅ 点赞:(0)

目录

1.Linux下编译大型项目的一般方法

2.make和makefile

区别

makefile的简单编写

依赖关系和依赖方法

清理方法

演示makefile中的多个依赖关系

简单理解make的自动化推导

make的默认选项

判断可执行文件是否最新的方法

Linux下的三种时间类型

stat命令

三种时间类型

修改时间(Modification Time,mtime)

改变时间(Change Time,ctime)

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

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

方法1:改变修改时间

方法2: .PHONY修饰

简写符号

$@和%^

@


 

1.Linux下编译大型项目的一般方法

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

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

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

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

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

tree redis

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

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

cd redis
make

全自动化构建

一段时间后完成编译

2.make和makefile

区别

make是命令,而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 test
clean:#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 test
step:
    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)简单提到过

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: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,例如:

.PHONY:test
test:test.c
    gcc test.c -o test
.PHONY:clean
clean:
    rm -rf test    

这样就能多次make了:

简写符号

$@和%^

可以使用$@ %^来简写

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