Makefile 是一种用来自动化编译、构建和管理软件项目的工具,它有很多好处,利用好Makefile可以让日常的工作流程更加简化和高效,以下是根据我日常使用习惯总结的一些Makefile的技巧
假设有三个源文件:main.c
、utils.c
和对应的头文件 utils.h
。
CC = gcc
CFLAGS = -Wall -g # 编译选项 -Wall 用于显示所有警告,-g 用于添加调试信息。
TARGET = myprogram # 目标可执行文件名
# 指定编译规则
$(TARGET): main.o utils.o
$(CC) $(CFLAGS) -o $(TARGET) main.o utils.o
main.o: main.c utils.h
$(CC) $(CFLAGS) -c main.c
utils.o: utils.c utils.h
$(CC) $(CFLAGS) -c utils.c
在Makefile中,头部可以定义变量和她对应的值,例如上面的CC,CFLAG,TARGET,而在下面就可以以$(变量名)的方式来调用
$(TARGET): main.o utils.o 表示 myprogram 的依赖文件是 main.o 和 utils.o,通过这两个文件来生成最终的可执行文件。main.o 和 utils.o 的规则定义了如何生成对应的 .o 文件。例如,main.o 依赖于 main.c 和 utils.h,当这两个文件有改动时,通过指定的编译器和编译选项生成 main.o,最终执行make命令即可完成这几个文件的编译操作。
Makefile的依赖关系:当你执行 make 命令时,Makefile 将会检查文件的依赖关系,如果某些源文件或头文件发生了改变,它会自动重新编译相关的部分,而不是重新编译整个项目。
在不同的平台其实编译的语句和方式都基本不差,差别只是在一些系统命令的调用
CC = gcc
CFLAGS = -Wall -g
RM = rm -f # linux平台下的删除
EXE = # linux平台下的生成文件
SPACE = echo "" # linux平台下的换行
ifeq ($(OS),Windows_NT)
RM = del /Q
EXE = .exe
SPACE = @echo.
endif
?例如上面的一些删除换行和生成文件的后缀等,不同的平台有所区分,可以用一个if判断来识别当前的系统状态如何改变变量
其实每次的编译和链接都会产生很多不必要的产出,如中间文件和可执行文件,我们可以通过定义简单的删除命令来删除他们
# 清理生成的文件
clean:
rm -f $(TARGET) *.o
# 根据系统环境执行删除生成的可执行文件命令
$(RM) $@$(EXE)
可以使用.PHONY来自定义命令实现批量执行多个操作
.PHONY: build clean docs test
build: compile docs test
compile: main.c utils.c
gcc -o myprogram main.c utils.c
docs:
doxygen Doxyfile
test:
./run_tests.sh
clean:
rm -f myprogram
# 还可以添加其他清理操作,比如删除文档文件等
build 是一个伪目标,它依赖于 compile、docs 和 test 三个操作;compile 是编译的操作,它依赖于 main.c 和 utils.c;docs 是生成文档的操作,使用 doxygen 工具根据配置文件 Doxyfile 来生成文档;test 是运行测试的操作,假设有一个脚本 run_tests.sh 用来执行测试;clean 是用来清理生成的可执行文件和其他可能生成的文件;通过执行 make build,Makefile 将按照定义的顺序执行 compile、docs 和 test 操作
可以用%来表示对应的文件名,%.c表示任意文件名的.c文件,都会依据CC指定的编译器在CFLAG这个附加参数下编译产生文件,@可以作为局部的变量指代生成文件
%: %.c # 编译所有.c文件
$(CC) $(CFLAGS) $< -o $@ # 生成文件为@
./$@$(EXE) # 执行生成文件
$(RM) $@$(EXE) # 删除生成文件