簡體   English   中英

Makefile 基於目標的依賴

[英]Makefile dependencies based on target

我有一個 Makefile,在變量INPUT_FILES中有用戶指定的輸入文件。

對於每個輸入文件,我需要創建一個輸入文件 prime。 一些注意事項:

  • 每個輸入文件可以有一個任意文件位置
  • 假設沒有重復的文件名是合理的
  • 每個 output 文件需要將 go 放入$(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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM