【0基础嵌入式学习日志】Day05:Makefile 自动化、通配符、模式规则与增量编译 Day06:头文件依赖追踪、.d 文件与完整增量编译
2026/8/5 11:00:05 网站建设 项目流程

【0基础嵌入式学习日志】Day05:Makefile 自动化、通配符、模式规则与增量编译

一、前言

今天继续进行嵌入式 C 语言基础学习。前面几天已经完成了 C 工程结构、结构体、故障码、函数封装、多文件拆分、.h/.c配合以及.c → .o → 可执行文件的分步编译流程。

Day04 中,Makefile 已经可以将多个.c文件分别编译成.o文件,再链接生成最终可执行程序:

src/main.c → build/main.o src/system.c → build/system.o src/sensor.c → build/sensor.o src/fault.c → build/fault.o build/main.o + build/system.o + build/sensor.o + build/fault.o → build/day04_test

但是 Day04 的 Makefile 仍然存在一个问题:每个.o文件和对应的编译规则都需要手动写出来。随着工程文件数量增加,这种写法会越来越麻烦。

因此,Day05 的重点是学习 Makefile 的自动化写法,包括:

wildcard patsubst 模式规则 自动变量 $< 和 $@ 增量编译

目标是让 Makefile 自动查找src目录下所有.c文件,并自动生成对应的.o文件。


二、Day05 学习目标

本次 Day05 主要学习以下内容:

  1. 理解为什么要进一步优化 Makefile;
  2. 学会使用wildcard自动查找.c文件;
  3. 学会使用patsubst自动生成.o文件列表;
  4. 理解模式规则build/%.o: src/%.c
  5. 理解自动变量$<$@的含义;
  6. 验证 Makefile 的增量编译机制;
  7. 理解修改单个.c文件后只重新编译对应.o文件的过程。

三、Day05 工程结构

Day05 工程是在 Day04 的基础上复制并修改得到的,整体结构如下:

day05 ├── Makefile ├── include │ ├── fault_code.h │ ├── fault.h │ ├── sensor.h │ ├── system.h │ └── system_type.h ├── src │ ├── fault.c │ ├── main.c │ ├── sensor.c │ └── system.c └── build ├── fault.o ├── main.o ├── sensor.o ├── system.o └── day05_test

其中:

  • include:存放头文件;
  • src:存放源文件;
  • build:存放编译生成的.o文件和可执行文件;
  • Makefile:管理自动化编译、运行和清理;
  • README.md:记录项目说明。

需要注意的是,build目录中的.o文件和day05_test是本地编译产物,一般不需要上传到 GitHub。


四、为什么要优化 Day04 的 Makefile?

Day04 的 Makefile 中手动写出了每一个目标文件:

OBJS = build/main.o build/system.o build/sensor.o build/fault.o

并且每一个.c文件都要单独写一条编译规则:

build/main.o: src/main.c $(CC) $(CFLAGS) -c src/main.c -o build/main.o build/system.o: src/system.c $(CC) $(CFLAGS) -c src/system.c -o build/system.o build/sensor.o: src/sensor.c $(CC) $(CFLAGS) -c src/sensor.c -o build/sensor.o build/fault.o: src/fault.c $(CC) $(CFLAGS) -c src/fault.c -o build/fault.o

这种方式虽然清楚,但是不够灵活。

如果后面新增一个源文件,例如:

src/uart.c

就需要手动修改 Makefile,增加:

build/uart.o

以及对应的编译规则。

真实嵌入式工程中,.c文件可能很多,如果每个文件都手动维护,效率会很低,也容易出错。

因此,Day05 使用更加自动化的 Makefile 写法,让它自动完成:

自动查找 src 目录下所有 .c 文件 自动生成 build 目录下对应的 .o 文件 使用一条模式规则完成所有 .c 文件的编译

五、Day05 Makefile 内容

Day05 的 Makefile 内容如下:

CC = gcc CFLAGS = -Wall -Wextra -Iinclude TARGET = build/day05_test SRCS = $(wildcard src/*.c) OBJS = $(patsubst src/%.c, build/%.o, $(SRCS)) all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) build/%.o: src/%.c $(CC) $(CFLAGS) -c $< -o $@ run: $(TARGET) ./$(TARGET) clean: rm -f build/*.o $(TARGET)

这里和 Day04 最大的区别是下面三部分:

SRCS = $(wildcard src/*.c) OBJS = $(patsubst src/%.c, build/%.o, $(SRCS)) build/%.o: src/%.c $(CC) $(CFLAGS) -c $< -o $@

这几句是 Day05 的核心。


六、Makefile 逐句理解

1. 指定编译器

CC = gcc

CC表示 C Compiler,也就是 C 语言编译器。

后面使用:

$(CC)

就等价于:

gcc

这样写的好处是:如果以后想换成其他编译器,只需要修改CC的值。


2. 指定编译参数

CFLAGS = -Wall -Wextra -Iinclude

其中:

  • -Wall:开启常见警告;
  • -Wextra:开启更多警告;
  • -Iinclude:告诉 GCC 到include目录查找头文件。

因为代码中包含了自己写的头文件:

#include"system.h"#include"sensor.h"#include"fault.h"

这些头文件都放在include目录下,所以需要使用:

-Iinclude

3. 指定最终可执行文件

TARGET = build/day05_test

这表示最终生成的可执行文件是:

build/day05_test

运行时可以使用:

./build/day05_test

七、wildcard:自动查找源文件

SRCS = $(wildcard src/*.c)

wildcard的作用是自动查找符合条件的文件。

这里:

$(wildcard src/*.c)

表示查找src目录下所有.c文件。

如果src目录中有:

src/fault.c src/main.c src/sensor.c src/system.c

那么:

SRCS = $(wildcard src/*.c)

最终相当于:

SRCS = src/fault.c src/main.c src/sensor.c src/system.c

这样以后如果新增一个:

src/uart.c

Makefile 会自动找到它,不需要手动修改SRCS


八、patsubst:自动生成目标文件列表

OBJS = $(patsubst src/%.c, build/%.o, $(SRCS))

patsubst是模式替换函数。

它的作用是把SRCS中的.c文件路径转换成对应的.o文件路径。

例如:

src/main.c → build/main.o src/system.c → build/system.o src/sensor.c → build/sensor.o src/fault.c → build/fault.o

也就是说:

OBJS = $(patsubst src/%.c, build/%.o, $(SRCS))

最终等价于:

OBJS = build/fault.o build/main.o build/sensor.o build/system.o

这样就不需要手动写每一个.o文件了。


九、模式规则:一条规则编译所有 .c 文件

Day04 中每个.c文件都需要单独写规则。

Day05 中使用一条模式规则:

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

这句表示:

所有 build/xxx.o 都可以由 src/xxx.c 编译得到

例如:

build/main.o 由 src/main.c 生成 build/system.o 由 src/system.c 生成 build/sensor.o 由 src/sensor.c 生成 build/fault.o 由 src/fault.c 生成

这里的%可以理解为匹配相同的文件名。

例如:

build/sensor.o: src/sensor.c

其中%就对应sensor


十、自动变量 $< 和 $@

在模式规则中使用了两个自动变量:

$< $@

它们的含义是:

$< 表示当前规则中的第一个依赖文件 $@ 表示当前规则中的目标文件

例如当前规则是:

build/sensor.o: src/sensor.c

那么:

$< = src/sensor.c $@ = build/sensor.o

所以:

$(CC) $(CFLAGS) -c $< -o $@

就等价于:

gcc-Wall-Wextra-Iinclude-csrc/sensor.c-obuild/sensor.o

这样一条规则就可以适用于所有.c → .o的编译过程。


十一、生成最终程序

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

这句表示:

要生成 build/day05_test 必须先生成所有 .o 文件

展开后大概等价于:

gcc build/fault.o build/main.o build/sensor.o build/system.o-obuild/day05_test

这一步是链接过程,也就是把多个.o文件链接成最终可执行程序。


十二、运行与清理

1. 运行程序

run: $(TARGET) ./$(TARGET)

执行:

makerun

等价于:

./build/day05_test

2. 清理编译产物

clean: rm -f build/*.o $(TARGET)

执行:

makeclean

会删除:

build/*.o build/day05_test

也就是删除所有.o目标文件和最终可执行程序。


十三、编译与运行

进入 Day05 目录:

cd/root/Embedded_14Days/day05

清理旧编译产物:

makeclean

编译:

make

查看build目录:

lsbuild

可以看到:

day05_test fault.o main.o sensor.o system.o

运行程序:

makerun

运行结果如下:

LED state:0Voltage:9.50V Current:2.50A temperature:72.00C Fault code: 0x0007 Fault: Low voltage Fault: Over current Fault: Over temperature

说明 Makefile 已经可以自动完成:

查找源文件 生成目标文件 链接可执行程序 运行程序

十四、增量编译验证

Day05 另一个重点是验证增量编译。

首先,在没有修改任何文件的情况下,再次执行:

make

终端输出:

make: Nothing to bedonefor'all'.

这说明当前所有目标文件都是最新的,不需要重新编译。

然后修改src/sensor.c中的温度值:

sys->temperature=72.0f;

改成:

sys->temperature=75.0f;

保存后再次执行:

make

此时终端只重新编译了:

gcc-Wall-Wextra-Iinclude-csrc/sensor.c-obuild/sensor.o

然后重新链接:

gcc build/fault.o build/main.o build/sensor.o build/system.o-obuild/day05_test

这说明 Makefile 判断出只有sensor.c被修改,所以只重新生成了:

build/sensor.o

而没有重新编译:

main.c system.c fault.c

这就是增量编译。


十五、修改后的运行结果

再次执行:

makerun

运行结果如下:

LED state:0Voltage:9.50V Current:2.50A temperature:75.00C Fault code: 0x0007 Fault: Low voltage Fault: Over current Fault: Over temperature

可以看到温度已经从72.00 C变成了75.00 C,说明修改后的sensor.c已经重新编译并生效。


十六、Day04 和 Day05 的区别

Day04 的 Makefile 是手动列出目标文件和规则:

OBJS = build/main.o build/system.o build/sensor.o build/fault.o

并且每个.c文件都要单独写一条规则。

Day05 的 Makefile 使用自动化写法:

SRCS = $(wildcard src/*.c) OBJS = $(patsubst src/%.c, build/%.o, $(SRCS)) build/%.o: src/%.c $(CC) $(CFLAGS) -c $< -o $@

也就是说:

Day04:手动列出每个 .o 文件和编译规则 Day05:自动查找 .c 文件,自动生成 .o 文件,并用一条模式规则统一编译

Day05 的 Makefile 更适合后续扩展工程。


十七、遇到的问题和解决方法

1. 目录路径不能直接当命令执行

学习过程中曾经直接输入:

/root/Embedded_14Days/day05

终端提示:

Is a directory

原因是目录路径本身不是命令,不能直接执行。

正确进入目录的方式是:

cd/root/Embedded_14Days/day05

2. git add 的路径要看当前所在目录

在上传 GitHub 时,如果当前已经在day05目录中,再执行:

gitaddday05

Git 会去找:

day05/day05

自然找不到。

更清楚的做法是先回到总目录:

cd/root/Embedded_14Days

然后执行:

gitaddday05

这样路径就不会出错。


十八、今日总结

通过 Day05 的学习,主要掌握了以下内容:

  1. 学会了使用wildcard自动查找src目录下所有.c文件;
  2. 学会了使用patsubst自动将.c文件列表转换成.o文件列表;
  3. 理解了模式规则build/%.o: src/%.c
  4. 理解了自动变量$<$@的作用;
  5. 学会了使用一条规则编译多个.c文件;
  6. 验证了 Makefile 的增量编译机制;
  7. 理解了修改单个.c文件后,只需要重新编译对应.o文件;
  8. 进一步理解了真实 C 工程中的自动化构建流程。

Day05 的核心可以总结为一句话:

Makefile 自动查找源文件, 自动生成目标文件列表, 使用模式规则统一编译, 并支持增量编译。

这一步对于后续学习嵌入式工程、驱动模块拆分、复杂 Makefile 和交叉编译非常重要。


十九、项目源码

本次 Day05 学习代码已上传至 GitHub:

https://github.com/jdai10590-afk/Embedded-C-Learning-Projects/tree/main/day05

【0基础嵌入式学习日志】Day06:头文件依赖追踪、.d 文件与完整增量编译

一、前言

今天继续进行嵌入式 C 语言基础学习。前面几天已经逐步完成了 C 工程结构、函数封装、多文件拆分、Makefile 自动化、.c → .o → 可执行文件的分步编译流程,以及通过wildcardpatsubst和模式规则实现自动化编译。

Day05 中,Makefile 已经可以自动完成:

自动查找 src 目录下所有 .c 文件 自动生成 build 目录下对应的 .o 文件 使用一条模式规则编译所有 .c 文件 修改某个 .c 文件后,只重新编译对应的 .o 文件

但是 Day05 还存在一个问题:它主要追踪.c文件的变化。如果修改的是.h头文件,Makefile 不一定知道哪些.o文件需要重新编译。

因此,Day06 的重点是学习头文件依赖追踪,也就是让 Makefile 能够知道:

某个 .o 文件不仅依赖对应的 .c 文件, 还依赖它所包含的 .h 头文件。

本次学习通过 GCC 的-MMD -MP参数自动生成.d依赖文件,并在 Makefile 中引入这些.d文件,从而实现更完整的增量编译。


二、Day06 学习目标

本次 Day06 主要学习以下内容:

  1. 理解为什么.h文件变化也会影响编译;
  2. 理解.d依赖文件的作用;
  3. 学会使用-MMD -MP自动生成头文件依赖信息;
  4. 学会通过DEPS自动生成.d文件列表;
  5. 学会使用-include $(DEPS)将依赖文件引入 Makefile;
  6. 验证修改头文件后,相关.o文件能够自动重新编译;
  7. 进一步理解真实 C 工程中的完整增量编译机制。

三、Day06 工程结构

Day06 工程是在 Day05 基础上复制并修改得到的,工程结构如下:

day06 ├── Makefile ├── include │ ├── fault_code.h │ ├── fault.h │ ├── sensor.h │ ├── system.h │ └── system_type.h ├── src │ ├── fault.c │ ├── main.c │ ├── sensor.c │ └── system.c └── build ├── fault.d ├── fault.o ├── main.d ├── main.o ├── sensor.d ├── sensor.o ├── system.d ├── system.o └── day06_test

其中:

  • include:存放头文件;
  • src:存放源文件;
  • build:存放.o目标文件、.d依赖文件和最终可执行程序;
  • Makefile:管理编译、链接、运行和清理过程;
  • README.md:记录项目说明。

相比 Day05,Day06 的build目录中多出了.d文件,例如:

fault.d main.d sensor.d system.d

这些.d文件就是今天学习的重点。


四、为什么需要头文件依赖追踪?

在 C 工程中,.h头文件通常会存放:

宏定义 结构体定义 函数声明 枚举类型 寄存器地址 配置参数 故障码定义

例如本项目中的fault_code.h用来定义故障码:

#ifndefFAULT_CODE_H#defineFAULT_CODE_H#defineFAULT_NONE0x0000#defineFAULT_LOW_VOLTAGE0x0001#defineFAULT_OVER_CURRENT0x0002#defineFAULT_OVER_TEMP0x0004#defineFAULT_SENSOR_ERROR0x0008// sensor fault#endif

这个头文件会被多个.c文件间接或直接使用。

例如:

system.c 使用 fault_code.h 中的 FAULT_NONE、FAULT_LOW_VOLTAGE 等宏 fault.c 使用 fault_code.h 中的故障码宏定义

如果修改了fault_code.h,那么依赖它的.c文件应该重新编译。

但是 Day05 的规则主要是:

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

这条规则只告诉 Makefile:

build/xxx.o 依赖 src/xxx.c

并没有告诉 Makefile:

build/system.o 还依赖 include/system.h、include/system_type.h、include/fault_code.h

所以 Day06 要解决的问题就是:

让 Makefile 自动知道每个 .o 文件到底依赖哪些 .c 和 .h 文件。

五、.d 文件是什么?

.d文件是依赖文件。

它的作用是记录:

某个 .o 文件依赖哪些 .c 文件和 .h 文件。

例如,编译system.c后会生成:

build/system.o build/system.d

其中system.o是目标文件,system.d是依赖文件。

查看system.d

catbuild/system.d

可以看到类似内容:

build/system.o: src/system.c include/system.h include/system_type.h \ include/fault_code.h include/system.h: include/system_type.h: include/fault_code.h:

这表示:

build/system.o 依赖 src/system.c build/system.o 依赖 include/system.h build/system.o 依赖 include/system_type.h build/system.o 依赖 include/fault_code.h

因此,如果这些文件中的任何一个发生变化,Makefile 就知道build/system.o需要重新编译。

这就是.d文件的作用。


六、Day06 Makefile 内容

Day06 的 Makefile 内容如下:

CC = gcc CFLAGS = -Wall -Wextra -Iinclude DEPFLAGS = -MMD -MP TARGET = build/day06_test SRCS = $(wildcard src/*.c) OBJS = $(patsubst src/%.c, build/%.o, $(SRCS)) DEPS = $(patsubst src/%.c, build/%.d, $(SRCS)) all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) build/%.o: src/%.c $(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@ run: $(TARGET) ./$(TARGET) clean: rm -f build/*.o build/*.d $(TARGET) -include $(DEPS)

相比 Day05,Day06 主要新增了三部分:

DEPFLAGS = -MMD -MP DEPS = $(patsubst src/%.c, build/%.d, $(SRCS)) -include $(DEPS)

并且编译规则中增加了:

$(DEPFLAGS)

七、Makefile 关键语句解释

1. 编译器设置

CC = gcc

CC表示 C Compiler,也就是 C 编译器。

后面写:

$(CC)

就等价于:

gcc

2. 编译参数

CFLAGS = -Wall -Wextra -Iinclude

其中:

  • -Wall:开启常见警告;
  • -Wextra:开启更多警告;
  • -Iinclude:告诉 GCC 到include目录查找头文件。

因为本项目的头文件都放在:

day06/include

所以需要通过:

-Iinclude

指定头文件搜索路径。


3. 依赖生成参数

DEPFLAGS = -MMD -MP

这是 Day06 的新增内容。

其中:

-MMD:让 GCC 自动生成项目头文件依赖信息 -MP:为头文件生成伪目标,避免头文件被删除后 Makefile 报错

简单理解:

-MMD -MP 的作用就是让 GCC 在编译 .c 文件时,同时生成 .d 依赖文件。

例如编译:

gcc-Wall-Wextra-Iinclude-MMD-MP-csrc/system.c-obuild/system.o

会同时生成:

build/system.o build/system.d

4. 最终目标文件

TARGET = build/day06_test

表示最终生成的可执行程序是:

build/day06_test

5. 自动查找源文件

SRCS = $(wildcard src/*.c)

wildcard用于自动查找src目录下所有.c文件。

例如:

src/fault.c src/main.c src/sensor.c src/system.c

都会被自动加入SRCS


6. 自动生成 .o 文件列表

OBJS = $(patsubst src/%.c, build/%.o, $(SRCS))

这句会把.c文件路径转换成.o文件路径。

例如:

src/main.c -> build/main.o src/system.c -> build/system.o src/sensor.c -> build/sensor.o src/fault.c -> build/fault.o

最终OBJS就是:

build/fault.o build/main.o build/sensor.o build/system.o

7. 自动生成 .d 文件列表

DEPS = $(patsubst src/%.c, build/%.d, $(SRCS))

这句和OBJS很像。

区别是:

OBJS 生成 .o 文件列表 DEPS 生成 .d 文件列表

例如:

src/main.c -> build/main.d src/system.c -> build/system.d src/sensor.c -> build/sensor.d src/fault.c -> build/fault.d

最终DEPS包含:

build/fault.d build/main.d build/sensor.d build/system.d

8. 编译目标

all: $(TARGET)

当输入:

make

时,默认会执行第一个目标all

这句表示:

make 的目标是生成 build/day06_test。

9. 链接最终程序

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

这表示:

要生成 build/day06_test,必须先生成所有 .o 文件。

展开后大概等价于:

gcc build/fault.o build/main.o build/sensor.o build/system.o-obuild/day06_test

这一步叫链接,也就是把多个.o文件链接成最终可执行程序。


10. 模式规则生成 .o 和 .d

build/%.o: src/%.c $(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@

这句表示:

所有 build/xxx.o 都可以由 src/xxx.c 编译得到。

例如:

build/system.o 由 src/system.c 编译得到 build/fault.o 由 src/fault.c 编译得到

其中:

$< 表示输入文件,也就是 src/xxx.c $@ 表示输出目标,也就是 build/xxx.o

因为加入了:

$(DEPFLAGS)

所以每次编译.c文件时,不仅生成.o,还会生成.d


11. 运行程序

run: $(TARGET) ./$(TARGET)

执行:

makerun

会运行:

./build/day06_test

12. 清理编译产物

clean: rm -f build/*.o build/*.d $(TARGET)

执行:

makeclean

会删除:

build/*.o build/*.d build/day06_test

也就是删除目标文件、依赖文件和最终可执行程序。


13. 引入依赖文件

-include $(DEPS)

这是 Day06 最关键的一句。

它的作用是:

把所有 .d 文件包含进 Makefile, 让 Makefile 知道每个 .o 文件还依赖哪些 .h 文件。

前面的-表示:

如果 .d 文件暂时不存在,也不要报错。

因为第一次执行make时,.d文件还没有生成。如果直接写:

include $(DEPS)

第一次可能会因为找不到.d文件而报错。

所以更稳妥的写法是:

-include $(DEPS)

八、编译与运行

进入 Day06 目录:

cd/root/Embedded_14Days/day06

清理旧文件:

makeclean

编译:

make

查看build目录:

lsbuild

可以看到:

day06_test fault.d fault.o main.d main.o sensor.d sensor.o system.d system.o

运行程序:

makerun

运行结果如下:

LED state:0Voltage:9.50V Current:2.50A temperature:75.00C Fault code: 0x0007 Fault: Low voltage Fault: Over current Fault: Over temperature

这说明程序编译和运行都正常。


九、查看 .d 文件内容

执行:

catbuild/system.d

可以看到类似:

build/system.o: src/system.c include/system.h include/system_type.h \ include/fault_code.h include/system.h: include/system_type.h: include/fault_code.h:

这一段说明:

build/system.o 依赖 src/system.c build/system.o 依赖 include/system.h build/system.o 依赖 include/system_type.h build/system.o 依赖 include/fault_code.h

因此,如果修改include/fault_code.h,Makefile 就知道system.o需要重新编译。

后面这些内容:

include/system.h: include/system_type.h: include/fault_code.h:

-MP自动生成的伪目标,主要用于避免头文件被删除后 Makefile 报错。


十、头文件依赖验证

为了验证头文件依赖是否生效,修改:

day06/include/fault_code.h

将:

#defineFAULT_SENSOR_ERROR0x0008

改成:

#defineFAULT_SENSOR_ERROR0x0008// sensor fault

保存后,再执行:

make

终端输出中可以看到:

gcc-Wall-Wextra-Iinclude-MMD-MP-csrc/fault.c-obuild/fault.o gcc-Wall-Wextra-Iinclude-MMD-MP-csrc/system.c-obuild/system.o gcc build/fault.o build/main.o build/sensor.o build/system.o-obuild/day06_test

这说明修改fault_code.h后,Makefile 自动判断出:

fault.o 依赖 fault_code.h system.o 依赖 fault_code.h

所以重新编译了:

src/fault.c -> build/fault.o src/system.c -> build/system.o

没有重新编译:

src/main.c src/sensor.c

这说明头文件依赖追踪已经生效。


十一、Day05 和 Day06 的区别

Day05 主要解决的是.c文件变化:

修改 sensor.c ↓ 重新编译 sensor.o ↓ 重新链接 day05_test

Day06 进一步解决的是.h文件变化:

修改 fault_code.h ↓ Makefile 根据 .d 文件判断哪些 .o 依赖它 ↓ 重新编译 fault.o 和 system.o ↓ 重新链接 day06_test

也就是说:

Day05:支持源文件级别的增量编译 Day06:支持头文件依赖级别的增量编译

Day06 更接近真实 C 工程和嵌入式工程中的构建方式。


十二、遇到的问题和解决方法

1. -MD 和 -MMD 的区别

一开始编译命令中使用过:

-MD-MP

后来改成:

-MMD-MP

两者区别是:

-MD:会记录系统头文件和项目头文件 -MMD:主要记录项目头文件,不记录系统标准库头文件

例如<stdio.h>这类系统头文件,一般不需要在项目依赖文件中追踪。

所以本项目最终使用:

DEPFLAGS = -MMD -MP

这样更加适合当前工程。


2. .d 文件第一次不存在怎么办?

第一次执行make时,.d文件还没有生成。

如果直接写:

include $(DEPS)

可能会因为找不到.d文件而报错。

因此使用:

-include $(DEPS)

前面的-表示:即使文件暂时不存在,也不要报错。


3. build 文件夹中的 .o 和 .d 不需要上传 GitHub

build目录中的文件属于编译产物,例如:

fault.o main.o sensor.o system.o fault.d main.d sensor.d system.d day06_test

这些文件可以通过源码和 Makefile 重新生成,一般不需要上传 GitHub。

GitHub 上主要保留:

Makefile README.md include/*.h src/*.c

十三、今日总结

通过 Day06 的学习,主要掌握了以下内容:

  1. 理解了.h头文件变化也会影响.o文件;
  2. 理解了.d文件用于记录目标文件依赖关系;
  3. 学会了使用-MMD -MP自动生成.d文件;
  4. 学会了通过DEPS自动生成依赖文件列表;
  5. 学会了使用-include $(DEPS)将依赖文件引入 Makefile;
  6. 验证了修改头文件后,相关.o文件会自动重新编译;
  7. 进一步理解了真实 C 工程中的完整增量编译机制。

Day06 的核心可以总结为一句话:

.d 文件记录每个 .o 文件依赖哪些 .c 和 .h 文件, Makefile 通过包含 .d 文件,实现头文件级别的增量编译。

这一步对于后续学习嵌入式工程、驱动开发、模块化编程、复杂 Makefile 和交叉编译非常重要。


十四、项目源码

本次 Day06 学习代码已上传至 GitHub:

https://github.com/jdai10590-afk/Embedded-C-Learning-Projects/tree/main/day06

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询