make
—— 项目的关节
一个中型或大型项目,通常由许多文件组成模块,又由各个模块组成整个系统,将各个文件、模块构建为系统的方式,一直是项目管理中的要点。make
是一个传统——但是流行的构建工具,Makefile
是其定义规则的文件,其中定义的规则就像关节,将项目中的各部分灵活而有序地组织在一起。
基本语法
Makefile
的核心是规则,其由一系列规则组成,每个规则的基本语法如下:
target: prerequisites
recipe
-
target:目标,即要创建或更新的文件,也可以是一个伪目标(不对应实际文件,用于执行特定操作)。 -
prerequisites:依赖项,是目标所依赖的文件或其他目标。当依赖项中的任何一个发生改变时, make
会认为目标需要更新,并执行对应的规则命令。 -
recipe:规则命令序列,是为了创建或更新目标而需要执行的命令,必须以制表符(Tab)开头。
除此之外,Makefile
还有一些作为脚本必要且实用的功能,以下的实际案例将进一步说明。
C语言项目构建案例
CC = gcc
CFLAGS = -Wall -g
all: myprogram
myprogram: main.o utils.o
$(CC) $(CFLAGS) -o myprogram 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
clean:
rm -f *.o myprogram
以上Makefile
编写了5条规则。
1. all
make
默认会执行Makefile
里首个规则的目标,此目标通常被称作默认目标。一般而言,会把all
作为默认目标,该目标依赖于项目中的其他主要目标,以此来确保所有必要的文件都能被构建。
make
也可手动指定构建目标,如:
make all
make clean
make main.o
2. myprogram
myprogram
目标有2个依赖项main.o
和utils.o
,若依赖项已构建完毕,则会执行recipe
部分所定义的命令,将.o
文件通过gcc
链接为可执行文件myprogram
。
对于每个目标,make
会检查其依赖项是否存在或者是否有更新。若依赖项不存在,make
会尝试构建这些依赖项;若依赖项的修改时间比目标新,make
会认为目标需要更新,进而执行该目标对应的规则命令。
3. main.o
若main.c
或utils.h
文件有更新,则根据recipe
定义的命令,通过gcc
编译生成main.o
。
4. utils.o
同main.o
。
5. clean
目标可以是文件,也可以是一个伪目标,伪目标可通过.PHONY
声明,确保即使存在同名文件,make
也会执行对应的规则。(案例中由于不存在与目标all/clean同名文件,故省略.PHONY
声明)
.PHONY: all clean
all: myprogram
clean:
rm -f *.o myprogram
当执行伪目标时,make
会直接执行该目标对应的规则命令,而不会检查文件的修改时间.
更进一步
Makefile
作为一种构建脚本,可以像shell
那样,在recipe
中直接编写命令,比如调用Python
脚本。
C
和Python
混合项目案例
假设项目中有一个C程序和一个Python脚本,C程序生成的数据会被Python脚本处理。
project/
├── main.c
├── process.py
└── Makefile
# 定义编译器和编译选项
CC = gcc
CFLAGS = -Wall -g
# 定义目标
all: data.txt result.txt
# 编译 C 程序并生成数据文件
data.txt: main
./main > data.txt
main: main.c
$(CC) $(CFLAGS) -o main main.c
# 使用 Python 脚本处理数据文件
result.txt: data.txt process.py
python3 process.py data.txt > result.txt
# 清理生成的文件
clean:
rm -f main data.txt result.txt
结语
Makefile
还有一些常用的高级语法,后续有机会我会再开一篇文章介绍。感兴趣的朋友可以点赞支持哦~
参考资料
GNU make: https://www.gnu.org/software/make/manual/make.html