本文对 makefile 进行简单的介绍。
问题引入
首先,为什么需要 makefile,以下面这个 C++ 文件为例:
// run.cc
#include <iostream>
int main() {
std::cout << "Hello MakeFile!" << '\n';
return 0;
}
我们可以使用 g++ run.cc -o output
将源文件编译链接为可执行文件,然后执行./output
输出 Hello MakeFile
。但这样有几个问题:
- 每次编译,需要重复编写上述命令;
- 随着项目的体量的增加,文件数量会变多,依赖关系会更复杂,难以维护。
MakeFile 则被设计用于解决上述问题。
MakeFile 语法介绍
makefile 的语法十分简单:
target ...: prerequisites ...
recipe
...
...
每一个这样的代码块被称为一条规则,用于描述目标名称、目标所依赖的文件和生成目标所需执行的命令。
- target: 生成的文件名或执行的命令名,如 clean
- prerequisite: 目标 target 所依赖的文件
- recipe: 生成目标 target 所需执行的命令
然后,使用 make
命令即可编译指定的目标,这样,
- 无需重复编写命令;
- 依赖关系隐含在目标所依赖的文件之中;
- 在构建时,
make
还会自动判断makefile
中的哪些目标需要重新编译,显著减少编译所需的时间。
需要注意的是,makefile 并不负责编译,编译是由其所执行的命令完成的,比如对于 C/C++,我们还必须使用 GCC或 clang 编译器,makefile 的最大作用是管理目标和命令。
默认目标
默认情况下,make
执行 makefile 中的第一个目标。
要修改默认目标,可以修改变量 .DEFAULT_GOAL
的值。
伪目标
clean:
rm *.o
伪目标没有依赖,仅用于执行一些命令,如 clean
用于删除所有的 .o
目标文件。
可以使用 .PHONY
显式指示该目标为伪目标。
定义变量
MakeFile 中支持定义变量。
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
一个例子:多文件
看一个 makefile 的例子。
run: run.o hello.o
g++ run.o hello.o -o run
run.o: run.cc
g++ -c run.cc
hello.o: hello.cc
g++ -c hello.cc
clean:
rm run *.o
这样,对于目标 run.o
,只有 run.cc
变了,make run.o
才会执行其下面的命令,对于目标 hello.o
,只有 hello.cc
变了,make hello.o
才会执行其下面的命令,run.o
或者 hello.o
变了,make run
会执行其下面的命令。
make clean
则用于清除目标。
另一个例子:存在外部依赖
最后,我们看一个有外部依赖的例子,项目文件结构如下。
├─Dependencies
│ │
│ └─GLFW
│ │
│ ├─include
│ │ └─GLFW
│ │ glfw3.h
│ │ glfw3native.h
│ │
│ └─lib
│ libglfw3.a
│
└─OpenGL
│
└─src
Application.cpp
其中 Application.cpp
依赖于 GLFW
,那么,此时根目录 MakeFile 应为:
app: Application.o
g++ Application.o -LDependencies/GLFW/lib -lglfw3 -lX11 -lOpenGL -o app
Application.o: OpenGL/src/Application.cpp
g++ -IDependencies/GLFW/include -c OpenGL/src/Application.cpp
clean:
rm *.o app
其中 g++
编译器的 -L
用于指定链接目录,-l
指定链接库,-I
指定头文件目录。
结语
以上是对 MakeFile 的简单介绍,如果有任何疑问,欢迎留言指出。
视频版BV号为:BV1j14y1v7hL。