Makefile 基础教程¶
本节目标
理解课程 Makefile 的核心结构,掌握常用构建命令,并能定位常见配置问题。
这份教程面向第一次接触 Makefile 的同学。目标是先把项目“跑起来”,再逐步理解它背后的规则。
1 Makefile 是什么¶
Makefile 用来描述“如何从源文件生成可执行结果”。
在当前项目里,可以把它理解成这样一条流水线:
- 编译
.c/.S得到.o - 链接得到
kernel.elf - 打包文件系统镜像
fs.img - 用
qemu运行内核
2 先掌握最常用命令¶
建议同学们先记住下面几条,实验里会反复使用:
这个 Makefile 支持临时改参数:
对应定义是:
?= 表示“如果外部没有给值,就使用默认值”。
3 Makefile 的基本结构¶
核心格式是:
例如:
意思是:all 依赖 kernel.elf 和 $(COMDB)。执行 make 时,会先把依赖准备好。
再看一个链接规则:
kernel.elf: $(KLD)/kernel.ld $(KOBJS) | $(COMDB)
$(LD) $(LDFLAGS) -T $(KLD)/kernel.ld -o $@ $(KOBJS)
这条规则负责把对象文件链接成 kernel.elf。
4 变量写法(入门够用)¶
当前文件里主要用了 4 种赋值方式:
=:普通赋值(递归展开):=:立即展开赋值(常和$(shell ...)配合)+=:在原变量后追加?=:变量未定义时才赋值
示例:
5 模式规则与自动变量¶
下面这条是批量编译的关键:
$(BUILD)/%.o: %.c
@mkdir -p $(@D) $(COMDBDIR)
COMPDB_DIR=$(COMDBDIR) COMPDB_FILE=$< $(CC) $(CFLAGS) -c -o $@ $<
重点看这几个符号:
%.c -> %.o:同名源文件都可以套用这条规则$@:当前目标文件$<:第一个依赖文件$(@D):目标文件所在目录
有了模式规则,就不用给每个 .c 文件重复写编译命令。
6 条件判断:检查环境是否可用¶
当前 Makefile 会自动检查工具链和 QEMU:
ifndef TOOLPREFIX
TOOLPREFIX := $(shell ...)
endif
ifeq ($(TOOLPREFIX),***)
$(error "Couldn't find a RISC-V toolchain in PATH")
endif
如果环境没配好,make 会直接报错并停止。这是正常行为,先按报错补环境即可。
7 .PHONY 与依赖自动包含¶
文件末尾有两句值得认识:
.PHONY:声明这些目标是“命令名”,不是实际文件名。-include ...d:包含自动生成的头文件依赖,改了.h后也能触发重编译。
8 一个最常见的修改场景¶
如果你新增了一个用户程序(例如 user/hello2.c),通常需要改两处:
- 在
UPROGS里加入hello2 - 在
fsimg目标中加入--add hello2=$(UBUILD)/hello2.elf
然后执行:
9 常见问题提醒¶
- 命令前缩进不是 Tab(Makefile 命令行必须是 Tab)
- 新程序写好了,但忘了加入
UPROGS - 工具链或
qemu-system-riscv64没装好 - 只看最后一行报错,忽略了前面的关键信息
建议提问时附上完整命令和完整报错输出,这样助教和同学更容易定位问题。