1. 程式人生 > >Makefile的個人理解以及簡單例子

Makefile的個人理解以及簡單例子

makefile的作用

用來進行條件編譯來實現對工程編譯的優化

基本規則

TARGET...:DEPENDENCE ...

                   COMMAND

                    ...

  • 目標:target程式產生的檔案,如可執行那個檔案和目標檔案;目標也可以是要執行的動作,如:“clean”
  • 依賴:dependence是用來產生目標的輸入檔案,一個目標通常依賴於多個檔案。

  • 命令:command是make執行的動作,一個target可以有多個命令。

Attention:每個命令必須只佔一行,而且必須要以TAB鍵開頭(固定格式)

makefile的核心內容

如果dependence中有一個或多個檔案更新的話,command就要執行

make的工作方式

  1. 找到檔案“Makefile”或者“makefile”
  2. 最終目標為第一個目標(也就是生成main)
  3. 如果main檔案不存在或者是有.o檔案有更新,那麼它會執行後面的指令來生產main檔案
  4. 看看哪個.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是最複雜的那種,此處說的複雜是需要每次手動加入相應的新檔案和它們的依賴。不可取。

三個自動化變數

  1. [email protected]  規則的目標檔名

  2. $<   規則的第一個依賴檔名

  3. $^   規則的所有依賴檔案列表

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):,其實只是會寫也不太懂具體是為什麼會導致這個錯誤,總之要記一下。