在开发嵌入式系统或C/C++项目时,经常要面对多个源文件的编译问题。比如你写了一个打印模块,包含 printer.c、buffer.c 和 main.c,每次手动敲 gcc 命令既费时又容易出错。这时候,Makefile 就派上用场了。
一个简单的项目结构
假设你的项目目录长这样:
project/
├── main.c
├── printer.c
├── printer.h
├── buffer.c
└── buffer.h
目标是生成一个叫 print_tool 的可执行文件。不靠 Makefile 的话,你得输入一长串命令:
gcc -c main.c -o main.o
gcc -c printer.c -o printer.o
gcc -c buffer.c -o buffer.o
gcc main.o printer.o buffer.o -o print_tool
一旦文件多了,改一个就得重新全部编译,效率极低。
写个最基础的Makefile
在项目根目录创建 Makefile 文件,内容如下:
print_tool: main.o printer.o buffer.o
gcc main.o printer.o buffer.o -o print_tool
main.o: main.c
gcc -c main.c -o main.o
printer.o: printer.c printer.h
gcc -c printer.c -o printer.o
buffer.o: buffer.c buffer.h
gcc -c buffer.c -o buffer.o
保存后,在终端运行 make,它会自动检查依赖关系,只编译改动过的文件。比如你只改了 printer.c,make 就只重新生成 printer.o,再链接一次。
用变量简化重复内容
上面的写法重复太多。可以定义变量来管理对象文件和编译器:
CC = gcc
OBJS = main.o printer.o buffer.o
TARGET = print_tool
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET)
main.o: main.c
$(CC) -c main.c -o main.o
printer.o: printer.c printer.h
$(CC) -c printer.c -o printer.o
buffer.o: buffer.c buffer.h
$(CC) -c buffer.c -o buffer.o
这样以后加新文件,只要往 OBJS 里添就行,维护起来方便不少。
引入通配规则减少样板
更进一步,可以用 %.o : %.c 这种模式规则,省去每个 .o 文件的手动声明:
CC = gcc
OBJS = main.o printer.o buffer.o
TARGET = print_tool
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET)
%.o: %.c
$(CC) -c $< -o $@
这里 $< 代表依赖文件(.c),$@ 代表目标文件(.o)。虽然这种方式不处理头文件依赖,但对简单项目已经够用。
实际应用场景:打印机固件更新
想象你在公司负责一台热敏打印机的固件开发。每次修复一个小bug,都要重新编译整个工程。用 Makefile 后,团队成员只需执行 make,就能快速生成新固件,烧录测试也更高效。
还能加些快捷目标,比如:
clean:
rm -f *.o print_tool
flash:
make
./burn_tool print_tool
输入 make clean 清理中间文件,make flash 一键编译并烧录,流程顺畅很多。