2017-01-01 53 views
1

我遇到了難以定義非遞歸製造系統的通用規則。使用一個變量來定義一個遞歸擴展變量,其名稱是從一個簡單的擴展變量中計算出來的

背景

對於進一步的閱讀,而不是我複製了太多的現有材料,見this earlier question,覆蓋地面相當不錯,和之前構建這個系統的時候幫助了我。我想要定義系統組件之間的依賴關係 - 例如,組件A依賴於組件B--然後離開make系統以確保B構建過程的任何產品在構建過程需要構建之前就已經被構建,因爲它的粒度有點浪費(可能會構建一些不需要的中間件),但對於我的用例來說,它在易用性和構建性能之間達到了一個舒適的平衡點。

系統必須處理的一個難題是makefile加載的順序無法控制 - 事實上,這應該不重要。但是,正因爲如此,在早期加載的makefile中定義的組件可能依賴於尚未讀取的makefile中定義的組件。

要允許通用模式應用於所有組件定義生成文件,每個組件都使用如下變量:$(component_name)_SRC。這是非遞歸(但遞歸包含)make系統的常見解決方案。

有關GNU make的不同類型變量的信息,請參閱the manual。總之:簡單擴展變量(SEV)在讀取makefile時展開,表現出類似命令式編程語言的行爲;遞歸擴展變量(REV)在make的第二階段中被擴展,在所有makefile被讀取之後。

的問題

試圖把依賴項組件列表進入這些組件代表文件的列表時,具體的問題出現。

我已經將我的代碼提煉成了這個可運行的例子,它留下了很多真實系統的細節。我認爲這足以證明這個問題而不失其實質。

rules.mk:

$(c)_src    := $(src) 
$(c)_dependencies  := $(dependencies) 

### This is the interesting line: 
$(c)_dependencies_src := $(foreach dep, $($(c)_dependencies), $($(dep)_src)) 

$(c) : $($(c)_src) $($(c)_dependencies_src) 
     @echo $^ 

的Makefile:

.PHONY: foo_a.txt foo_b.txt bar_a.txt hoge_a.txt 

### Bar 
c   := bar 
src   := bar_a.txt 
dependencies := 

include rules.mk 

### Foo 
c   := foo 
src   := foo_a.txt foo_b.txt 
dependencies := bar hoge 

include rules.mk 

### Hoge 
c   := hoge 
src   := hoge_a.txt 
dependencies := bar 

include rules.mk 

這些將運行給:

$ make foo 
foo_a.txt foo_b.txt bar_a.txt 
$ 

hoge_a.txt不包括在輸出中,因爲在時間foo_dependencies被定義爲SEV,hoge_src尚不存在。

所有的makefiles被讀取後的擴展是REVs應該能夠解決的問題,我之前嘗試將$(c)_dependencies_src定義爲REV,但這不起作用,因爲$(c)然後在替換時擴展,而不是定義時間,所以它不再保持正確的價值。

如果有人想知道爲什麼我不使用特定於目標的變量,我擔心變量應用於手冊中描述的目標的所有先決條件將導致規則之間不同組件的不需要的交互。

我想知道:

  1. 有沒有這個具體問題的解決方案? (也就是說有一種簡單的方法可以讓這條線達到我想要的效果嗎?)
  2. 有沒有更像一個更典型的建造這種製造系統的方法? (即單個構建實例,從多個構建文件加載組件並定義這些組件之間的依賴關係)。
  3. 如果存在多個解決方案,它們之間的權衡是什麼?

最後的評論:正如我寫了我的問題,我已經開始意識到,可能有一個解決方案可能使用eval來構建REV定義,但是因爲我無法在任何地方找到此問題否則,爲了未來的搜索者,我認爲值得問這個問題,另外我想聽聽更多有經驗的用戶對這個或任何其他方法的想法。

+0

爲什麼在設置'$(c)_dependencies_src'時你使用':='而不是'='?如果你在這裏使用'=',它會解決你的問題。 – MadScientist

+0

個人而言,我永遠不會創建一個使用「全局」變量的系統,例如分配'c:= foo',然後包括規則文件,然後分配'c:= bar'幷包含規則文件。我會這樣做:'targets + = foo',然後'foo_c:= foo','foo_src:= foo.x'等等,然後'targets + = bar','bar_c:= bar','bar_src := bar.x'等。這樣可以避免設置相同的變量名稱並確保在重置之前使用它們。 – MadScientist

+0

您是否嘗試過使用我發佈的示例?它至少不適用於我,正如我所說:「... REVs應該能夠解決,我以前嘗試將$(c)_dependencies_src定義爲REV ...」 –

回答

0

簡短的回答是沒有很好的解決方案,你問的問題。在中途停止擴展變量是不可能的,並且推遲到最後。不僅如此,還因爲您在前提列表中使用該變量,即使您可以獲取$(c)_dependencies_src變量的值僅包含所需的變量引用,但在下一行中,它們將作爲先決條件列表的一部分完全展開所以它不會給你帶來任何好處。

推遲擴展先決條件只有一種方法,那就是使用secondary expansion功能。你將不得不這樣做:

$(c)_src    := $(src) 
$(c)_dependencies  := $(dependencies) 

.SECONDEXPANSION 
$(c) : $($(c)_src) $$(foreach dep, $$([email protected]_dependencies), $$($$(dep)_src)) 
     @echo $^ 

(未經測試)。這個問題通過$(c)_dependencies_src來解決,只是根本沒有定義它,直接把它放入先決條件列表中,而是作爲第二次擴展。

正如我在上面的評論中所寫的那樣,我個人不會設計一個像這樣工作的系統。我更喜歡使用一個系統,其中所有變量都是使用名稱空間(通常預先添加目標名稱)預先創建的,然後在最後定義所有變量之後,包括一個「rules.mk」或任何將使用的變量所有這些變量來構建規則,很可能(除非你所有的食譜都很簡單)使用eval

所以,像這樣:

targets := 

### Bar 
targets += bar 
bar_c   := bar 
bar_src   := bar_a.txt 
bar_dependencies := 

### Foo 
targets += foo 
foo_c   := foo 
foo_src   := foo_a.txt foo_b.txt 
foo_dependencies := bar hoge 

### Hoge 
targets += hoge 
hoge_c   := hoge 
hoge_src   := hoge_a.txt 
hoge_dependencies := bar 

# Now build all the rules 
include rules.mk 

然後在rules.mk你會看到類似這樣的:

define make_c 
$1 : $($1_src) $(foreach dep, $($1_dependencies), $($(dep)_src)) 
     @echo $$^ 
endif 

$(foreach T,$(targets),$(eval $(call make_c,$T))) 

你甚至可以擺脫設置target如果你小心你的變量名稱,加入類似rules.mk

targets := $(patsubst %_c,%,$(filter %_c,$(.VARIABLES))) 

爲了在不同的目標中允許相同的組件,您只需要在名稱空間中添加更多內容來區分這兩個不同的組件。

+0

謝謝您指出這是擴大變量的規則定義 - 我沒有意識到這一點。我確實理解你不喜歡這裏使用的不嚴格聲明的方法,但我仍然想看看是否有辦法讓我目前的方法有效,所以我會等待並看看是否有其他人想回復暫時的。在動態構建名稱空間時有一定的價值,因爲它將管理它的責任從組件用戶轉移到組件用戶,但是我很感謝這是一種折衷,這個問題是其中一個後果。 –