使用 Makefile 可以让我们免去编译、链接时,老是需要输入繁琐的命令。
使用案例#
假设现在在你的工作目录下有三个文件:main.cpp
, print.cpp
, calc.cpp
.
其中 main.cpp
中使用了 print.cpp
和 calc.cpp
的符号,现在我们想要用把它们编译、链接成一个可执行文件。
原始方法:(生成一个名为 holo 的可执行文件)
g++ -o holo main.cpp print.cpp calc.cpp
shell使用 Makefile:
Version 1#
在工作目录下新建文件 Makefile,输入如下:
holo: main.cpp print.cpp calc.cpp
g++ -o holo main.cpp print.cpp calc.cpp
makefile第一行的意思是要生成 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.cpp
makefile这个相对于 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
变量。