使用 Makefile 可以让我们免去编译、链接时,老是需要输入繁琐的命令。
使用案例#
假设现在在你的工作目录下有三个文件:main.cpp, print.cpp, calc.cpp.
其中 main.cpp 中使用了 print.cpp 和 calc.cpp 的符号,现在我们想要用把它们编译、链接成一个可执行文件。
原始方法:(生成一个名为 holo 的可执行文件)
g++ -o holo main.cpp print.cpp calc.cppshell使用 Makefile:
Version 1#
在工作目录下新建文件 Makefile,输入如下:
holo: main.cpp print.cpp calc.cpp
g++ -o holo main.cpp print.cpp calc.cppmakefile第一行的意思是要生成 holo 对象,冒号后面是为了生成 holo 对象需要的依赖,第二行的意思就是执行具体的命令(注意前面加了 Tab 符!)
然后我们只需要在工作目录下执行 make holo 命令就可以生成对应的可执行文件了!(实际上直接用 make 也可以,因为如果不指定生成对象的话,make 会自动找到第一个对象进行生成)
如果 holo 对象的生成时间是更新于它所有的依赖文件的,那么我们在这种情况下执行 make 的时候是不会执行对应的命令的,因为你的对象已经是最新的了。
Version 2#
CXX = g++
TARGET = holo
OBJ = main.o print.o calc.o
$(TARGET): $(OBJ)
$(CXX) -o $(TARGET) $(OBJ)
main.o: main.cpp
$(CXX) -c main.cpp
print.o: print.cpp
$(CXX) -c print.cpp
calc.o: calc.cpp
$(CXX) -c calc.cppmakefile这个相对于 Version 1 的优势是
- 定义了一些变量作为可替换的值,增加了可维护性。
- 对于 TARGET 的所需要的依赖,在 Makefile 文件中都写出了这些依赖所需的依赖,这样我们就可以根据依赖们的时间来推断是否这个依赖(这里指 TARGET 所需的 .o 文件)需要被重新编译。这样就实现了按需编译,减少了不必要的时间。
Version 3#
CXX = g++
TARGET = holo
OBJ = main.o print.o calc.o
CXXFLAGS = -c -Wall
$(TARGET): $(OBJ)
$(CXX) -o $@ $^
%.o: %.cpp
$(CXX) -o $@ $(CXXFLAGS) $<
.PHONY: clean
clean:
rm -rf *.o $(TARGET)makefile这里的 $@ 指前面的生成对象名,$^ 指生成该对象的所有依赖,$< 指生成该对象的第一个依赖。
.PHONY 的作用:如果没有 .PHONY: clean,那么如果你的工作目录里面如果有 clean 文件的话,那么执行 make clean 是不会执行下面的命令了,因为 clean 文件已经是最新的了。所以我们添加 .PHONY: clean 来向 make 说明 clean 并不是实际的生成对象,而是一套指令,直接执行即可。
这个相对于 Version 2 的优势是
- 对于所有的 .o 对象,定义它的依赖为它的
前缀.cpp,所以对于每次新添加的文件,要修改的地方更少了。 - 定义了
make clean,执行它可以清除所有由make产生的文件。
Version 4#
CXX = g++
TARGET = holo
SRC = $(wildcard *.cpp)
OBJ = $(patsubst %.cpp, %.o, $(SRC))
CXXFLAGS = -c -Wall
$(TARGET): $(OBJ)
$(CXX) -o $@ $^
%.o: %.cpp
$(CXX) -o $@ $(CXXFLAGS) $<
.PHONY: clean
clean:
rm -rf *.o $(TARGET)makefile这个版本和 Version 3 的区别就是对变量 SRC 和 OBJ 的定义。
这个相对于 Version 3 的优势是
SRC = $(wildcard *.cpp)会自动查找工作目录下(不会递归查找工作目录下的目录,如有需要可以配合find命令)的所有 cpp 文件,然后赋值给SRC变量。OBJ = $(patsubst %.cpp, %.o, $(SRC))会将SRC的所有 .cpp 文件名替换为对应的 .o 文件名,然后赋值给OBJ变量。