欢迎光临
我们一直在努力

make / Makefile

目录:

  • 1. make/Makefile的使用
  • 2. make 与 Makefile 的关系
    • 2.1 角色分工
    • 2.2 工作流程
  • 3. Makefile 的核心
  • 4. make 的工作
  • 5. 伪目标 .PHONY
  • 6. 变量
    • 6.1. 链接规则
    • 6.2. 模式匹配规则,编译.c 生成.o
  • 7. 函数
  • 8. 使用示例

1. make/Makefile的使用

在写 C/C++ 项目时,如果每次改一个文件都要手敲:

gcc -c main.c -o main.o
gcc -c utils.c -o utils.o
gcc main.o utils.o -o app

文件一多,命令又长又重复,还容易漏掉某个文件、忘记重新编译。更麻烦的是:改了一个 .c 文件,其实只需要重新编译它,再链接一次就行,但手敲时往往全量重编,浪费时间。

make 就是解决这个问题的工具:它是一个自动化构建工具,读一个叫 Makefile 的脚本,按照里面写的规则,自动判断哪些文件需要重新编译,然后执行对应命令。

2. make 与 Makefile 的关系

一句话概括:

make 是可执行程序(命令工具),Makefile 是给 make 读取的配置脚本文件。make 读 Makefile,然后根据里面描述的规则,自动执行编译、构建等工作。

2.1 角色分工

  • make:程序,Linux 自带的构建工具。 它本身不知道怎么编译代码,只会去读取规则文件,解析规则,对比文件时间戳,执行 shell 命令。 你在终端敲的 make,就是运行这个程序。
  • Makefile:文本文件(文件名固定,首字母大写,也可以小写 makefile)。 里面写好:目标文件、依赖关系、要执行的命令。 它是给 make 看的 “说明书”,没有 Makefile,直接敲 make 会报错:No makefile found。
名称说明
make 可执行程序,构建工具本身
Makefile / makefile 描述构建规则的文本文件,make 读取它
规则(rule) Makefile 的基本单位,一条规则 = 目标 + 依赖 + 命令

2.2 工作流程

终端输入 make
↓
make程序 寻找当前目录 Makefile / makefile
↓
make 解析Makefile,读取里面的规则(目标:依赖 + 命令)
↓
比较【目标文件】和【依赖文件】的时间戳
– 如果依赖更新(修改过源码):执行命令,重新生成目标
– 如果目标最新:跳过,不执行(增量构建,只编译改动文件)

执行时直接在项目目录敲

make

make 会自动在当前目录找 Makefile 或 makefile(也可以 make -f 自定义文件名)。

3. Makefile 的核心

Makefile 最基本的结构:

目标(target): 依赖(prerequisites)
命令(recipe)

关键点:命令前面必须是一个 Tab,不能用空格!

一个最简单的例子:

app: main.o utils.o
gcc main.o utils.o -o app
main.o: main.c
gcc -c main.c -o main.o
utils.o: utils.c
gcc -c utils.c -o utils.o

含义:

  • app 依赖 main.o、utils.o,用 gcc 链接出 app

  • main.o 依赖 main.c,编译出 main.o

  • utils.o 依赖 utils.c,编译出 utils.o

4. make 的工作

make 的核心逻辑其实就两条:

  • 依赖关系:目标依赖哪些文件?要生成目标,先保证依赖都就绪。

  • 时间戳判断:如果目标的修改时间比任一依赖旧,说明依赖更新了,目标过期,需要重新执行命令;否则跳过。

  • 举例:app 依赖 main.o、utils.o。

    • 第一次 make:app 不存在 → 先递归生成 main.o、utils.o → 再链接 app。

    • 只改了 utils.c 后 make:utils.c 比 utils.o 新 → 重新编译 utils.o;main.o 没变 → 不重编;utils.o 比 app 新 → 重新链接 app。

    这就是 增量构建:只做必要的重编译,效率高。

    小实验:连续敲两次 make,第二次会提示 make: 'app' is up to date.,因为目标比依赖新。

    改 utils.c 再 make:

    屏幕只执行两条命令:

    5. 伪目标 .PHONY

    有些"目标"并不是真的文件,比如 clean、all、run。如果目录里恰好有个叫 clean 的文件,make 会以为目标已存在而不执行命令

    所以要用 .PHONY 声

    .PHONY: clean all
    all: app
    clean:
    rm -f *.o app

    6. 变量

    手写一堆 gcc、文件名容易重复,用变量维护

    # 通常放在最顶部
    TARGET = app # 最终生成的可执行程序名
    CC = gcc # 编译器
    CFLAGS = -Wall -g # 编译参数:开启警告 + 加入调试信息
    SRCS = main.c foo.c # 源码文件列表
    OBJS = $(SRCS:.c=.o)# 后缀替换,把 .c 全部换成 .o → main.o foo.o

    示例:

    CC = gcc
    CFLAGS = -Wall -g
    TARGET = app
    OBJS = main.o utils.o
    $(TARGET): $(OBJS)
    $(CC) $(OBJS) -o $(TARGET)
    %.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@
    .PHONY: clean
    clean:
    rm -f $(OBJS) $(TARGET)

    6.1. 链接规则

    $(TARGET): $(OBJS)
    $(CC) $(OBJS) -o $(TARGET)

    • $(TARGET):读取变量,代表最终可执行文件(比如app)
    • $(OBJS):所有的目标文件(main.o foo.o) 含义:可执行文件 TARGET 依赖所有 .o 文件。 当任意.o文件更新,执行下面命令:

    gcc main.o foo.o -o app

    作用:链接阶段,把多个.o目标文件合并,生成最终程序。

    6.2.模式匹配规则,编译.c 生成.o

    %.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

    • %:Makefile 的通配符,匹配任意文件名
    • %.o: %.c:代表:任何 xxx.o 文件,依赖同名的 xxx.c
    • $(CC):编译器 gcc
    • $(CFLAGS):编译选项(警告、调试选项)
    • -c:只编译,不链接,生成.o目标文件
    • $<:自动变量,代表规则里第一个依赖文件(也就是xxx.c)
    • $@:自动变量,代表当前规则的目标文件(也就是xxx.o)

    示例:当需要main.o,make 会自动匹配这条规则,执行:

    gcc -Wall -g -c main.c -o main.o

    6.3 伪目标 clean,清理编译产物

    .PHONY: clean
    clean:
    rm -f $(OBJS) $(TARGET)

    .PHONY: clean 声明 clean 是伪目标:它不是磁盘上的文件,只是一个动作。

    如果当前目录恰好存在一个名叫clean的文件,不加.PHONY时,make clean会判断:文件已经存在且最新,不会执行删除命令。加上就可以避免这个 bug。

    clean: 目标 执行 make clean 触发下面 shell 命令:

    rm -f main.o foo.o app

    • rm:删除命令
    • -f:强制删除,文件不存在时不会报错 作用:删除所有.o中间文件和可执行程序,清空编译产物。

    变量总结:

    符号含义
    $(TARGET) 最终可执行程序(自定义变量)
    $(OBJS) 所有.o 目标文件(自定义变量)
    $(CC) 编译器(一般 gcc)
    $(CFLAGS) 编译选项
    % 通配符,匹配任意文件名
    $< 第一个依赖文件
    $@ 当前规则的目标文件

    7. 函数

    Makefile 自带一些函数,最常用的几个:

    SRCS = $(wildcard *.c) # 找出所有 .c 文件
    OBJS = $(SRCS:.c=.o) # 把 .c 后缀替换成 .o

    • $(wildcard *.c):通配符展开

    • $(SRCS:.c=.o):后缀替换

    这样新加 .c 文件不用改 Makefile:

    CC = gcc
    CFLAGS = -Wall -g
    TARGET = app
    SRCS = $(wildcard *.c)
    OBJS = $(SRCS:.c=.o)
    $(TARGET): $(OBJS)
    $(CC) $^ -o $@
    %.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@
    .PHONY: clean
    clean:
    rm -f $(OBJS) $(TARGET)

    8. 使用示例

    BIN=xin.exe
    #SRC=$(shell ls *.c)
    SRC=$(wildcard *.c) # wildcard函数,获取当前目录下的所有的原文件
    OBJ=$(SRC:.c=.o)
    $(BIN):$(OBJ)
    @gcc -o $@ $^
    @echo "linking $^ to $@ … done"
    %.o:%.c
    @gcc -c $<
    @echo "compiling $< to $@ … done"
    .PHONY:clean
    clean:
    .PHONY:test
    test:
    @echo "Debug——-"
    @echo $(SRC);
    @echo "Debug——-"
    @echo $(OBJ);
    @echo "Debug——-"

    赞(0)
    未经允许不得转载:171主机测评 » make / Makefile
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址