个人Linux操作系统学习笔记4 - makefile

目录


makefile

在vs中,vs默认帮我们做了自动化构建

在linux中,我们要使用make/makefile自动化项目的构建

其中

  • make是一个命令
  • makefile是一个文件

简单展示Makefile

shell 复制代码
$ ls
test.c
$ touch Makefile
$ vim Makefile 
$ cat Makefile 
test.exe:test.c
    gcc -o test.exe test.c
$ make
gcc -o test.exe test.c
$ ls
Makefile  test.c  test.exe

make命令与makefile:依赖关系与依赖方法

test.exe:test.c表明依赖关系

gcc -o test.exe test.c表明依赖方法

makefile会形成推导栈

makefile 复制代码
test.exe:test.o
    gcc test.o -o test.exe
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
shell 复制代码
$ make
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -c test.s -o test.o
gcc test.o -o test.exe
$ ll
-rw-rw-r-- 1    157 Dec 14 21:31 Makefile
-rw-rw-r-- 1     74 Dec 14 19:47 test.c
-rwxrwxr-x 1   8440 Dec 14 21:31 test.exe
-rw-rw-r-- 1  16872 Dec 14 21:31 test.i
-rw-rw-r-- 1   1496 Dec 14 21:31 test.o
-rw-rw-r-- 1    447 Dec 14 21:31 test.s

会根据依赖关系形成推导栈 ,然后依次出栈

本质就是一种脚本

清理功能

在里面生成伪目标

shell 复制代码
$ cat Makefile
test.exe:test.c
    gcc -o test.exe test.c
.PHONY:clean
clean:
    rm -f test.exe test.i test.s test.o

执行clean命令

shell 复制代码
$ make clean
rm -f test.exe test.i test.s test.o
$ ls
Makefile  test.c
  • 细节1:依赖关系必须存在,依赖文件列表可以为空
  • 细节2:依赖方法可以是任何shell命令
  • 细节3 :clean目标,只是利用make的自动推导的能力,让他执行了rm命令
    在构建工程的视角,看起来就是在清理项目
    清理项目,本质就是删除不需要的临时文件
  • 细节4make命令,后面可以跟"目标名"
    后面跟谁,就解析谁的依赖关系和依赖方法
    make默认只会推导一条完整的推导链路
    make默认只会推导第一个依赖关系(最佳实践要把clean放后面)

其中,.PHONY 表示声明一个符号(名字自定义)

用于修饰目标文件是一个伪目标

本质:总是被执行的!

什么叫总是被执行的?

shell 复制代码
$ ls
Makefile  Makefile-backup  test.c
$ make
gcc -o test.exe test.c
$ make
make: `test.exe' is up to date.

Makefile中,test.exe:test.这句并没有被声明为总是被执行的

因此在完成一次之后,就不允许再执行

如果改为

makefile 复制代码
.PHONY:test.exe
test.exe:test.c
    gcc -o test.exe test

此时再执行:

shell 复制代码
$ ls
Makefile  Makefile-backup  test.c
$ make
gcc -o test.exe test.c
$ make
gcc -o test.exe test.c

就可以总是被执行

这也是为什么make clean可以一直被执行!

.PHONY是如何实现总是被执行的?

因为.c.exe的本质都是文件,那么它们的属性都会有时间

shell 复制代码
$ stat test.c
  File: 'test.c'
  Size: 74            Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 1186657     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1004/ suliiik)   Gid: ( 1005/ suliiik)
Access: 2025-12-14 19:51:24.398090793 +0800
Modify: 2025-12-14 19:47:18.493390146 +0800
Change: 2025-12-14 19:47:18.493390146 +0800
 Birth: -
$ stat test.exe 
  File: 'test.exe'
  Size: 8440          Blocks: 24         IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 1186660     Links: 1
Access: (0775/-rwxrwxr-x)  Uid: ( 1004/ suliiik)   Gid: ( 1005/ suliiik)
Access: 2025-12-14 22:03:49.973706809 +0800
Modify: 2025-12-14 22:03:49.973706809 +0800
Change: 2025-12-14 22:03:49.973706809 +0800
 Birth: -

如何判断源文件是否需要被重新编译?

源文件和可执行谁更新?

是通过比较两个文件的修改时间来比较的!!

.PHONY就是忽略了文件Mod时间这个条件,因此可以总是被执行!

如果希望更新一个文件的修改时间,可以

shell 复制代码
$ touch test.c

这样可以在不修改文件内容的前提下更新文件修改时间

关闭打印

现在每次执行make命令,都会打印出其中的命令(回显)

shell 复制代码
$ make
gcc -o test.exe test.c
$ make clean
rm -f test.exe test.i test.s test.o

这样既不优雅也不美观

Makefile里面给命令加上@之后就可以不打印了

makefile 复制代码
test.exe:test.c
    @gcc -o test.exe test.c
.PHONY:clean
clean:
    @rm -f test.exe test.i test.s test.o

此时不会打印任何信息:

shell 复制代码
$ vim Makefile
$ make clean
$ make

想要改变.c/.exe的文件名,一定要改Makefile吗?

假设我把test.c改名为code.c,则每次都要打开Makefile进行修改文件名字,非常麻烦

因此可以在开头定义变量

makefile 复制代码
BIN=test.exe
SRC=test.c

$(BIN):$(SRC)
    @gcc -o test.exe test.c
.PHONY:clean
clean:
    @rm -f test.exe test.i test.s test.o

在编译时,BINSRC会类似于宏替换一样代入$()的位置

但是上面的命令无法正常执行,因为@gcc -o test.exe test.c等命令是硬编码

因此,要改为变量名

makefile 复制代码
BIN=test.exe
SRC=test.c

$(BIN):$(SRC)
    @gcc -o $@ $^
.PHONY:clean
clean:
    @rm -f $(BIN)

其中,在gcc/g++中,@是一个特殊的内置变量

如果项目里面有大量.c文件该如何写makefile

方法一:使用shell命令

使用ls命令,将*.c文件全都写入SRC

makefile 复制代码
BIN=mycode.exe
SRC=$(shell ls *.c)

.PHONY:test
test:
    @echo "Debug------"
    @echo $(SRC)
    @echo "Debug------"

但是最佳实践并非如此

方法二:使用wildcard函数

wildcard函数:获取当前目录下所有的源文件

makefile 复制代码
BIN=mycode.exe
#SRC=$(shell ls *.c)
SRC=$(wildcard *.c)

.PHONY:test
test:
    @echo "Debug------"
    @echo $(SRC)
    @echo "Debug------"

想要生成.o文件怎么办

makefile 复制代码
BIN=mycode.exe
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)

$(BIN):$(OBJ)
    gcc -o $@ $^
%.o:%.c
    gcc -c $<

.PHONY:test
test:
    @echo "Debug------"
    @echo $(SRC)
    @echo "Debug------"
    @echo $(OBJ)
    @echo "Debug------"

%是一个通配符%.o相当于把所有.o文件展开

$<的意思是:将:右边的文件一个个对应左边的文件

会展开为

makefile 复制代码
main.o:main.c
  gcc -c main.c
src1.o:src1.c
  gcc -c src1.c
......

clean命令

makefile 复制代码
BIN=mycode.exe
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)

$(BIN):$(OBJ)
    @gcc -o $@ $^
%.o:%.c
    @gcc -c $<

.PHONY:clean
clean:
    @rm -f $(OBJ) $(BIN)

最佳实践

makefile 复制代码
BIN=mycode.exe
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
CC=gcc
Echo=echo
Rm=rm -rf

$(BIN):$(OBJ)
    @$(CC) -o $@ $^
    @$(Echo) "linking $^ to $@ ... done"
%.o:%.c
    @$(CC) -c $<
    @$(Echo) "compling $< to $@ ... done"


.PHONY:clean
clean:
    $(Rm) $(OBJ) $(BIN)

文件时间:

Access:文件被访问的更新时间(读取内容或执行)

Modify:文件内容的更新时间

Change:文件属性的更新时间

  • 为什么修改内容后 Change 时间也更新了?

    因为文件大小、inode 等元数据随之变化,触发了 Change 时间更新。

  • 为什么使用cat不一定会更新Access

    对于一个文件的操作,"查看"这一行为的占比极大

    如果每次查看操作都要更新时间,那么意味着每次查看都要修改文件属性

    那就意味着每次查看操作都需要刷新磁盘

    会增加访问磁盘的次数,增加IO,导致外设效率低下,OS整体效率降低

    现代OS优化后,会在 访问特定次数/间隔一段时间 后进行更新一次时间

补充背景知识

回车换行

回车:指回到当前行的最左侧

换行:移动到下一行

相关推荐
_童年的回忆_1 小时前
【php】在linux下PHP安装amqp扩展
linux·开发语言·php
sxlishaobin1 小时前
linux 自动清除日志 脚本
linux·服务器·前端
蚰蜒螟2 小时前
走进 Linux 内核:从 touch 命令到磁盘 inode 的完整旅程
java·linux·前端
feng_you_ying_li2 小时前
liunx之可重入函数,volatite,和线程的基本介绍(1)
linux
兮山与2 小时前
Linux
linux·javaee进阶
明夜之约2 小时前
Linux 系统命令使用速查手册
linux·运维
夜月yeyue3 小时前
KCP 与 UDP 可靠传输
linux·网络·单片机·网络协议·udp·php
比昨天多敲两行3 小时前
Linux信号
linux·运维·服务器
sulikey4 小时前
ELF文件中的“节“与“段“,如何与虚拟地址空间中的“分页“和“分段“产生联系?
linux·服务器·elf·虚拟地址空间·分页·分段·elf文件