进入大二以后天天写代码,不如就重新使用自己的blog吧。感觉看自己之前写的东西重新学习还挺不错的。
Makefile可以帮助你编译。原来搞OI的时候都是一个cpp文件直接编译运行就行了。不过现在假设你要写一个很简单的小计算器,每一个cpp文件执行一种算术方法,你如果一个一个编译的话,大概是要写成这样的:
g++ -c -Wall main.cpp -o main.o g++ -c -Wall add.cpp -o add.o g++ -c -Wall sub.cpp -o sub.o g++ -c -Wall mul.cpp -o mul.o g++ -c -Wall div.cpp -o div.o g++ -o main main.o add.o sub.o mul.o div.o
这样写起来很麻烦,所以Makefile应运而生。
Makefile的核心规则(核心语法?)大概是这样的:
targets : prerequisites
其中targets是目标文件(目前大多数你写的程序都只有一个执行文件,可以先理解为执行文件),prerequisites是用于生成target的文件或目标。
写一个比较简单的,只有3个cpp的makefile示例:
main : main.cpp printhello.cpp factorial.cpp g++ -o main main.cpp printhello.cpp factorial.cpp
在终端输入make就可以运行makefile了。之后./main来执行程序。
Makefile中使用变量其实很像C++中的宏(#define),大致写法是:
CC = g++ TARGET = main OBJ = main.o printhello.o factorial.o $(TARGET) : $(OBJ) $(CC) -o $(TARGET) $(OBJ)
其中,CC,TARGET,OBJ等赋值符号左边的叫做变量(variables),其实只看代码的话,和上一个是完全一样的,我们把宏替换一下就是一样的代码了。
这个主要是用于减少代码量,写起来更加简洁。
有的时候只有一个或几个cpp被改动,我们是不需要重新编译所有文件的。设想如果有上百个cpp,只改动其中一个就要全部重新编译显然非常浪费时间,因此我们可以将我们的代码改写一下:
CC = g++ TARGET = main OBJ = main.o printhello.o factorial.o main.o: main.cpp $(CC) -c main.cpp printhello.o: printhello.cpp $(CC) -c printhello.cpp factorial.o: factorial.cpp $(CC) -c factorial.cpp
假如我们只改变了main.cpp,那么重新执行make的时候他就只会重新编译main.cpp,而不会把所有cpp都重新编译一次了。
不过现在的问题是代码量增加了很多,如果有上百个cpp我们显然是吃不消的,这时候就需要使用文件依赖。大致意思是,每一个.o文件都是.cpp文件编译而成的。所以我们将代码采取如下写法:
CC = g++ TARGET = main OBJ = main.o printhello.o factorial.o CFLAGS = -c -Wall $(TARGET) : $(OBJ) $(CC) -o $@ $(OBJ) %.o : %.cpp $(CC) $(CFLAGS) $< -o $@ #本段中$@表示所有目标文件,$^表示所有prerequisites文件,$<表示第一个prerequsites文件。 #因为每一个.o依赖于一个.cpp,所以这里写$<或者$^都是可以的。 #注意这里前后两个$@表示的文件是不同的,第一个表示的是TARGET(main),即可执行文件,而第二个表示的这个.o文件对应的.cpp文件。
注意其中一行%.o : %.cpp,这一段是一个规则,大致意思就是我们的文件依赖,所有的.o都是依赖于.cpp文件的。
Makefile中的函数非常多,这里只简单介绍两种,wildcard和patsubst。
我们的代码已经很简洁了,但是看OBJ一行,如果我们有上百个.cpp,也就会有上百个.o,那手动输入进去还是很麻烦的。
解决方法就是使用wildcard(通配符)函数,这个函数可以自动搜索路径中的一类文件(我们这里先假定为cpp文件),用法如下:
SRC = $(wildcard ./*.cpp)
现在SRC就找到了路径中所有的.cpp文件,之后我们使用字符串函数patsubst,它可以批量处理字符串,我们的目标是把所有.cpp改成.o,用法如下:
SRC = $(wildcard ./*.cpp) OBJ = $(patsubst %.cpp, %.o, $(SRC)) target: @echo $(SRC) @echo $(OBJ)
target后面是输出语句,可以让你验证一下找的是不是正确的文件。
那么现在我们就可以非常简洁的写好makefile了。直接上最终版的代码吧。src是存储所有cpp文件的文件夹,inc是存储所有hpp(h)文件的文件夹,makefile文件应该和这两个文件夹处在一个子目录下。
#第一行这里写 = src也行 SRC_DIR = ./src SOURCE = $(wildcard $(SRC_DIR)/*.cpp) OBJ = $(patsubst %.cpp, %.o, $(SOURCE)) TARGET = main INCLUDE = -I./inc #这里是在inc里找所有引用的hpp文件 CC = g++ CFLAGS = -c -Wall $(TARGET) : $(OBJ) $(CC) -o $@ $(OBJ) %.o : %.cpp $(CC) $(CFLAGS) $< -o $@ $(INCLUDE) .PHONY : clean clean: rm -f $(SRC_DIR)/*.o $(TARGET)
最后.PHONY : clean这里是clean函数,用于清理多余的.o文件。使用方法是在终端输入make clean即可。
本文章只包含一些非常初级的makefile用法,如果想学更多,可以看这里或者这里。