Makefile的個人理解以及簡單例子
makefile的作用
用來進行條件編譯來實現對工程編譯的優化
基本規則
TARGET...:DEPENDENCE ...
COMMAND
...
- 目標:target程式產生的檔案,如可執行那個檔案和目標檔案;目標也可以是要執行的動作,如:“clean”
-
依賴:dependence是用來產生目標的輸入檔案,一個目標通常依賴於多個檔案。
-
命令:command是make執行的動作,一個target可以有多個命令。
Attention:每個命令必須只佔一行,而且必須要以TAB鍵開頭(固定格式)
makefile的核心內容
如果dependence中有一個或多個檔案更新的話,command就要執行
make的工作方式
- 找到檔案“Makefile”或者“makefile”
- 最終目標為第一個目標(也就是生成main)
- 如果main檔案不存在或者是有.o檔案有更新,那麼它會執行後面的指令來生產main檔案
- 看看哪個.o更新了,找到相應的target的command執行得到.o檔案,最後執行終極目標生成main。
偽目標
".PHONY"它的作用主要是防止檔名和自己的指令名重合,如果重合只執行為目標的命令。
.PHONY:clean
由例子來說明Makefile
#這是一個最簡陋的makefile的表達 main:main.o add.o minus.o #首先就是檢視下面的目標有沒有生成,生成之後最後將所有生成main工程的.o檔案找到(main.o add.o subtract.o)生成main工程。 gcc main.o add.o minus.o -o main main.o:main.c add.h minus.h #首先將標頭檔案和c檔案列出來 gcc -c main.c -o main.o #每一個.o檔案都需要編譯生成 add.o:add.c add.h gcc -c add.c -o add.o minus.o: minus.c minus.h gcc -c minus.c -o minus.o .PHONY:clean #偽指令防止重名 clean: rm -f main main.o add.o minus.o #對工程進行清理
上面的makefile是最複雜的那種,此處說的複雜是需要每次手動加入相應的新檔案和它們的依賴。不可取。
三個自動化變數
-
[email protected] 規則的目標檔名
-
$< 規則的第一個依賴檔名
-
$^ 規則的所有依賴檔案列表
makefile使用變數
#定義變數
object=main.o add.o minus.o #指令碼語言不需要變數型別
main:$(object) #在使用變數之前需要加$(變數名)
Makefile自動推導
GNU的make命令很厲害,可以根據要生成的.o檔案的名字找到相應的c檔案,並且也會自己推倒命令。所以我們可以將上面的一些命令去掉,剩下就是需要包含的一些標頭檔案。
#這個是自動推導指令的makefile公式
object=main.o add.o minus.o
main:$(object)
gcc $(object) -o main
main.o: add.h minus.h #只要標頭檔案
add.o: add.h
minus.o:minus.h
.PHONY:clean #偽指令防止重名
clean:
rm -f main main.o add.o minus.o
但是每一次建立一個新的檔案之後還是要將新的檔案加到object裡面,最後我們可以使用一些好用的Makefile函式
Makefile函式
- wildcard函式 :當前目錄下匹配模式的檔案,因為makefile會將萬用字元當成一個巨集定義,也就是想要獲取檔案的時候就不能使用萬用字元,但是我們可以通過wildcard函式,使用萬用字元 例如:src=$(wildcard *.c) 就是將所有的資料夾內部的c檔案獲取給src
- notdir函式 :去除路徑 例如:$(notdir $src)
- patsubst函式 :模式匹配替換 可以將搜尋到的.c檔案替換成.o檔案 例如:$(patsubst%.c,%.o,$src)
- 等價於$(src:.c=.o)
- shell函式 : 執行shell命令 例如:$(shell ls –d */)
ELF=main
CC=gcc
src=$(wildcard *.c) #通過wildcard找到所有的
objects=$(src:.c=.o) #找到src裡面所有的c檔案所對應的.o檔案
$(ELF):$(objects)
$(CC) $^ -o [email protected]
$(objects):
clean:
rm -f $(objects) $(ELF)
最後一個完整版的makefile,有許多的IDE都有專門幫你生成makefile的檔案的功能,像是Qt的Qmake,你開啟它們內部生成的一些makefile或者是.mk檔案,你就會發現,人家寫的很清晰(變數用的規範,好看)
為了好看,也為了學習,我就自己也模仿了一個
CXX = g++
CXXFLAG = -o2 -g -Wall
LDFLAGS = #這裡一般寫一些庫的路徑,可以配合pkg-config
SRC += $(wildcard *.cpp)
#按道理講人家會都把你新增到工程的.o檔案羅列在這裡,但是我比較懶就用函數了
OBJS = $(patsubst %.cpp,%.o,$(SRC)) #之前這裡的SRC寫成小寫的了,導致錯誤
LIBS += -lpthread \
-lboost_system
HEADER_DIR = -I.
TARGET = myserver
$(TARGET):$(OBJS)
$(CXX) $(OBJS) $(HEADER_DIR) -o $(TARGET) $(LIBS)
%.o:%.cpp
$(CXX) -c $< -o [email protected] $(LIBS)
$(OBJS): #關鍵不要忘記寫了 , 不然會出現問題生成不了OBJ
.PHONY:clean
clean:
rm -f $(OBJ) $(TARGET)
遇到一個bug就是居然找不到依賴也就是編譯的時候使用$^找不到,後來發現是偽指令宣告上面沒有加$(OBJS):,其實只是會寫也不太懂具體是為什麼會導致這個錯誤,總之要記一下。