Makefile快速入门
简介:
Makefile 是一种用于自动化构建和管理软件项目的工具文件,通常与
make
命令配合使用。它通过定义规则(rules)来指定如何从源文件生成目标文件(如可执行程序或库),并自动处理文件之间的依赖关系。Makefile 的核心思想是“仅重新构建需要更新的部分”,从而显著提高编译效率。
一、基础语法快速入门
由目标(target)、依赖(prerequisites)、命令(commands)三部分组成,如
目标:依赖命令 # 命令前必须以 Tab 缩进app: main.o utils.o gcc main.o utils.o -o app #将main.o和utils.o联合编译成app main.o: main.c gcc -c main.c -o main.o #将main.c编译生成main.o utils.o:utils.cgcc -c utils.c -o utils.o #将utils.c编译生成utils.o
运行 make app触发编译流程,此时将会生成main.o、utils.o和可执行文件app。
二、变量和自动变量的使用
简化重复值(如编译器路径或 flags)和简化命令中的重复输入,如:
app: main.o utils.o gcc main.o utils.o -o app $@: 当前目标名(app)。 $^: 所有依赖(main.o utils.o)。 $<: 第一个依赖文件(main.o)。#当然也可以替换为其他变量,类似于C语言中的变量+宏定义 CC = gcc CFLAGS = -Wall -O2 OBJS = main.o utils.o#将main.c编译生成main.o main.o: main.cgcc -Wall -O2 -c main.c -o main.o #第一种写法$(CC) $(CFLAGS) -c $< -o $@ #第二种写法#将main.o和utils.o联合编译成app app: main.o utils.o gcc main.o utils.o -o app #第一种写法$(CC) $(CFLAGS) $(OBJS) -o $@ #第二种写法$(CC) $(CFLAGS) $^ -o $@ #第三种写法
后缀替换:通过直接替换或者替换函数(如 wildcard, patsubst)简化操作:
# 获取所有 .c 文件 SRC = $(wildcard *.c) # 将 .c 替换为 .o OBJ = $(patsubst %.c, %.o, $(SRC))#直接后缀替换 将 .c 文件列表转为 .o文件列表 SRCS = main.c utils.c config.c OBJS = $(SRCS:.c=.o) #替换后结果:OBJS = main.o utils.o config.o
条件判断:一般用作不同的编译场景下,如:
ifeq ($(OS), Windows_NT)RM = del elseRM = rm -f endif或#FLAVOR = release FLAVOR = debugifeq ($(FLAVOR),debug)TARGET_LDFLAGS += -g -O2 elif ($(FLAVOR),release)TARGET_LDFLAGS += -g -O0 endif
通过编译器生成头文件依赖关系,避免手动维护
CFLAGS += -MMD # 生成 .d 依赖文件 -include $(OBJS:.o=.d) # 包含所有依赖描述文件
多目录项目构建
递归编译子目录:
SUBDIRS = driver user .PHONY: $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $@
# 进入子目录执行 make
使用
vpath
指定源文件搜索路径:vpath %.c src:lib
# 在 src 和 lib 目录中查找 .c 文件
变量与依赖调试
打印变量值:
debug: @echo "源文件列表:$(SRCS)" # @ 抑制命令回显
查看依赖树:
make -d | grep "Considering target"4。
并行编译加速
启用多线程编译(如4线程):
make -j4
固件生成规则
添加生成
.bin
文件的规则:firmware.bin: firmware.elfobjcopy -O binary $< $@ # 从 .elf 生成二进制固件
条件编译支持
DEBUG ?= 1 ifeq ($(DEBUG),1)CFLAGS += -g -DDEBUG endif
三、伪目标
定义不生成实际文件的操作(如
clean
),需用.PHONY
声明:# 清理构建文件 .PHONY: clean clean:rm -f *.o app
将生成的一些不需要的.o文件清理掉,此时并没有生成目标文件,所以用伪目标。