通用makefile是如何煉成的(VII)

上一次我們試圖引入多模塊,結果失敗了,今天我們來幫助失敗實現做母親的願望。在正式開始之前,先做一個孕前檢查

我們上一次的失敗原因有兩個

1.  編譯目標重定義。

2.  一些變量定義被覆蓋。

這些實際上是遞歸形式的多模塊makefile必然要遇到的兩個問題。

對症下藥的藥方就是makefile中的“多規則目標”(具體參看makefile必知必會,簡單而言就是三句話:

makefile允許目標的依賴關係通過多個規則定義,但是實現該目標的命令只能在一個規則中定義,實現時會自動合併該目標所有的依賴關係。

下面來解決第一個問題,clean目標重定義。既然clean目標的實現命令不能在多個規則中定義,那麼我們就爲clean目標建立一個依賴關係clean:$(TARGET).clean, 在依賴目標$(TARGET).clean中定義命令。

由於$(TARGET).clean,各個模塊必然不同,自然而然地就解決了目標重定義的問題。

# executable.mk

## 編譯生成obj文件的通用規則
include build/targets/object.mk
	
## 利用多規則定義,將clean目標轉發給$(TARGET).clean,從而避免出現clean目標重定義的問題
## 對all,不需要額外處理,因爲$(TARGET)本身就要求不同模塊目標不同
all:	$(TARGET)
clean: $(TARGET).clean

## 定義可執行文件hello.exe的編譯規則	
$(TARGET) :	$(OBJS) $(LIBS)
	$(LD) $(LDFLAGS)  -o $@ $^
	
## 定義 目標clean,一般就是刪除所有obj文件,以及可執行文件。
.PHONY: $(TARGET).clean
$(TARGET).clean:
	$(RM) $(TARGET) $(OBJS) $(DEPS)


下面來解決第二個問題,  首先稍微解釋一下這條規則$(ALL_TARGETS)  :          OBJS  := $(OBJS)   它的與衆不同在於依賴目標是一個賦值表達式。

這條規則的妙處在於能將變量的值與目標$(ALL_TARGETS)綁定起來。簡單說來,不管之前OBJS的值是什麼,當需要生成目標$(ALL_TARGETS)時,由於依賴關係,會先去計算“OBJS  := $(OBJS)“這條語句。那麼這裏面 $(OBJS)的值是怎麼算呢?它的值來源於makefile的第一次解析,因爲目標和依賴目標中的變量是立即展開的。於是乎

當makefile解析到模塊A時,假設ALL_TARGETS=a, OBJS=a.o, 這時上面的規則就變成 a : OBJS := a.o;

當解析到模塊B時, 假設ALL_TARGETS=b,  OBJS=b.o.  這時上面的規則變成b : OBJS :=b.o; 

然後makefile開始第二次掃描,  注意,在此之前OBJS的值已經是b.o了。當makefile去生成目標a時,會自動計算依賴目標,也就是表達式"OBJS := a.o", 於是OBJS又恰到好處的還原爲需要的值。

這個技術很像是變量存儲,因此我們新增了一個vars-stash.mk, 利用這個技術做變量存儲,防止被覆蓋。

# vars-stash.mk

$(info ALL_TARGETS=$(ALL_TARGETS))
$(ALL_TARGETS) : AR       := $(AR)
$(ALL_TARGETS) : ARFLAGS  := $(ARFLAGS)
$(ALL_TARGETS) : ARLIBS   := $(ARFLAGS)

$(ALL_TARGETS) : CC       := $(CC)
$(ALL_TARGETS) : CPP      := $(CPP)
$(ALL_TARGETS) : CXX      := $(CXX)
$(ALL_TARGETS) : CFLAGS   := $(CFLAGS)
$(ALL_TARGETS) : CPPFLAGS := $(CPPFLAGS)
$(ALL_TARGETS) : CXXFLAGS := $(CXXFLAGS)

$(ALL_TARGETS) : LD       := $(LD)
$(ALL_TARGETS) : LDLIBS   := $(LDLIBS)
$(ALL_TARGETS) : LDFLAGS  := $(LDFLAGS)

$(ALL_TARGETS) : OBJS  := $(OBJS)
$(ALL_TARGETS) : DEPS  := $(DEPS)
$(ALL_TARGETS) : TARGET  := $(TARGET)

在executable.mk中,直接包含vars-stash.mk

## 定義 目標clean,一般就是刪除所有obj文件,以及可執行文件。
.PHONY: $(TARGET).clean
$(TARGET).clean:
	$(RM) $(TARGET) $(OBJS) $(DEPS)

ALL_TARGETS := $(OBJS) $(TARGET) $(TARGET).clean
include build/targets/vars-stash.mk


至此,失敗終爲成功之母,簡單調整,多模塊的makefile就在襁褓之中。

爲hello文件新建一個module.mk, 配置好源文件列表,編譯目標,以及引用static-library.mk, 就可以正常編譯得到hello.a。同時修改主文件夾下的module.mk, 在依賴目標中增加hello.a

## main.mk

## 只要給定源文件目錄以及目標hello.exe
## 調用executable.mk,就可以自動編譯得到想要的可執行文件
SRC_FILES:= src/main.cpp
LDLIBS += src/hello/hello.a
TARGET:=hello.exe

## 定義瞭如何生成可執行文件的通用規則
include build/targets/executable.mk




今天的工作到此爲止。這一次的調整還是比較大的,中途也不太方便做測試。必須在調整完target之後,才能做多模塊化編譯。其實我中間也費了不少時間。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章