進入大二以后天天寫代碼,不如就重新使用自己的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應運而生。
1.基本規則
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來執行程序。
2.使用變量
Makefile中使用變量其實很像C++中的宏(#define),大致寫法是:
CC = g++
TARGET = main
OBJ = main.o printhello.o factorial.o
$(TARGET) : $(OBJ)
$(CC) -o $(TARGET) $(OBJ)
其中,CC,TARGET,OBJ等賦值符號左邊的叫做變量(variables),其實只看代碼的話,和上一個是完全一樣的,我們把宏替換一下就是一樣的代碼了。
這個主要是用於減少代碼量,寫起來更加簡潔。
3.文件依賴(我也不知道是不是該叫這個名字)
有的時候只有一個或幾個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文件的。
4.使用函數
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即可。