[英]Makefile dependencies based on target
我有一個 Makefile,在變量INPUT_FILES
中有用戶指定的輸入文件。
對於每個輸入文件,我需要創建一個輸入文件 prime。 一些注意事項:
$(OUTPUT_DIR)
我的基本策略是生成基於INPUT_FILES
的目標集,然后嘗試確定哪個輸入文件是目標的實際依賴項。
我嘗試過的一些變體:
# Create a list of targets
OUTPUT_FILES = $(foreach file,$(notdir $(INPUT_FILES)),$(OUTPUT_DIR)/$(file))
# This doesn't work, because all input files are dependencies of each output file
$(OUTPUT_FILES): $(INPUT FILES)
program --input $^ --output $@
# This doesn't work because $@ hasn't been resolved yet
$(OUTPUT_FILES): $(filter,$(notdir $@),$(INPUT FILES))
program --input $^ --output $@
# This doesn't work, I think because $@ is evaluated too late
.SECONDEXPANSION:
$(OUTPUT_FILES): $(filter,$(notdir $$@),$(INPUT FILES))
program --input $^ --output $@
# This doesn't work either
.SECONDEXPANSION:
$(OUTPUT_FILES): $$(filter,$(notdir $@),$(INPUT FILES))
program --input $^ --output $@
我也研究了 static 模式規則,但我不確定它是否可以幫助我滿足需要。
在您的情況下.SECONDEXPANSION:
有效,因為您可以使用 make 函數( filter
)來計算每個 output 文件的先決條件。 在其他情況下,這可能是不可能的。 但是還有另一個 GNU make 功能可以在像您這樣的情況下使用:如果您使用 GNU make,您可以使用foreach-eval-call
以編程方式實例化 make 語句。 請記住,用作語句模式的宏被擴展了兩次,這就是為什么你必須加倍一些$
符號的原因(稍后會詳細介紹):
OUTPUT_DIR := dir
OUTPUT_FILES := $(addprefix $(OUTPUT_DIR)/,$(notdir $(INPUT_FILES)))
.PHONY: all
all: $(OUTPUT_FILES)
# The macro used as statements pattern where $(1) is the input file
define MY_RULE
$(1)-output-file := $(OUTPUT_DIR)/$$(notdir $(1))
$$($(1)-output-file): $(1)
@echo program --input $$^ --output $$@
endef
$(foreach i,$(INPUT_FILES),$(eval $(call MY_RULE,$(i))))
演示:
$ mkdir -p a/a b
$ touch a/a/a b/b c
$ make INPUT_FILES="a/a/a b/b c"
program --input a/a/a --output dir/a
program --input b/b --output dir/b
program --input c --output dir/c
解釋:
當 make 解析 Makefile 時,它會擴展$(foreach...)
:它遍歷$(INPUT_FILES)
的所有單詞,對於每個單詞,它將單詞分配給變量i
並擴展$(eval $(call MY_RULE,$(i)))
在這種情況下。 因此,對於foo/bar/baz
這個詞,它擴展$(eval $(call MY_RULE,$(i)))
和i = foo/bar/baz
。
$(eval PARAMETER)
擴展PARAMETER
並將結果實例化為新的 make 語句。 因此,對於foo/bar/baz
,make 使用i = foo/bar/baz
擴展$(call MY_RULE,$(i))
並將結果視為常規 make 語句。 $(eval...)
的擴展沒有其他作用,結果是空字符串。 這就是為什么在我們的例子中$(foreach...)
擴展為空字符串。 但它做了一些事情:為每個輸入文件動態創建新的 make 語句。
$(call NAME,PARAMETER)
擴展PARAMETER
,將其分配給臨時變量1
並在此上下文中擴展 make 變量NAME
的值。 因此, $(call MY_RULE,$(i))
with i = foo/bar/baz
擴展為變量MY_RULE
with $(1) = foo/bar/baz
的擴展值:
foo/bar/baz-output-file:= dir/$(notdir foo/bar/baz) $(foo/bar/baz-output-file): foo/bar/baz @echo program --input $^ --output $@
這就是eval
實例化為新的 make 語句的內容。 請注意,我們在這里進行了第一次擴展, $$
變成$
。 另請注意, call
可以有更多參數: $(call NAME,P1,P2)
將對$(1) = P1
和$(2) = P2
執行相同的操作。
當 make 解析這些新語句時(與任何其他語句一樣),它會擴展它們(第二次擴展)並最終將以下內容添加到其變量列表中:
foo/bar/baz-output-file:= dir/baz
及其規則列表中的以下內容:
dir/baz: foo/bar/baz @echo program --input $^ --output $@
這可能看起來很復雜,但如果您還記得eval
添加的 make 語句被擴展了兩次,那就不復雜了。 第一次是$(eval...)
被 make 解析和擴展,第二次是 make 解析和擴展添加的語句。 這就是為什么您經常需要使用$$
而不是$
來轉義宏定義中這兩個擴展中的第一個。
它是如此強大,很高興知道。
當尋求幫助時,請提供一些實際的示例名稱,以便我們可以更清楚地了解您有什么。 它還可以幫助我們使用不會混淆的術語。
你真的想在你的食譜中使用$<
,而不是$^
,我想。
如果您的“輸入文件”確實是僅輸入的(也就是說,它們本身不是由其他 make 規則生成的),那么您可以使用 VPATH輕松解決此問題。
只需使用這個:
VPATH := $(sort $(dir $(INPUT_FILES)))
$(OUTPUT_DIR)/% : %
program --input $< --output $@
我終於找到了一個可行的排列——我認為問題是忘記了filter
需要%
來匹配模式。 規則是:
.SECONDEXPANSION:
$(OUTPUT_FILES): $$(filter %$$(@F),$(INPUT_FILES))
program --input $^ --output $@
我還意識到我可以使用@F
(相當於$$(notdir $$@)
)來獲得更簡潔的語法。
該規則在其第二次擴展 ( $$(@F)
) 中獲取目標的文件名,然后在第二次擴展 ( $$(filter %$$(@F),$(INPUT_FILES))
)。
當然,該規則僅在文件名唯一時才有效。 如果有人有更清潔的解決方案,請隨時發布。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.