Makefile的初學教程


進入大二以后天天寫代碼,不如就重新使用自己的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
commands

其中targets是目標文件(目前大多數你寫的程序都只有一個執行文件,可以先理解為執行文件),prerequisites是用於生成target的文件或目標。
表示輸入一個Tab縮進(這個非常重要!!),commands 是你要執行的命令(shell命令)。

寫一個比較簡單的,只有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即可。

本文章只包含一些非常初級的makefile用法,如果想學更多,可以看這里或者這里


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM