【问题标题】:Makefile dependencies based on target基于目标的 Makefile 依赖
【发布时间】:2021-02-24 00:55:39
【问题描述】:

我在变量 INPUT_FILES 中有一个包含用户指定输入文件的 Makefile。

对于每个输入文件,我需要创建一个输入文件素数。 一些注意事项:

  • 每个输入文件都可以有任意文件位置
  • 假设没有重复的文件名是合理的
  • 每个输出文件都需要进入$(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 $@

我也研究过静态模式规则,但我不确定它是否能满足我的需要。

【问题讨论】:

    标签: makefile dependencies target


    【解决方案1】:

    我终于找到了一个有效的排列 - 我认为问题在于忘记了 filter 需要 % 来匹配模式。规则是:

    .SECONDEXPANSION:
    $(OUTPUT_FILES): $$(filter %$$(@F),$(INPUT_FILES))
      program --input $^ --output $@
    

    我还意识到我可以使用@F(相当于$$(notdir $$@))来获得更简洁的语法。

    该规则在第二次扩展时获取目标的文件名 ($$(@F)),然后在第二次扩展 ($$(filter %$$(@F),$(INPUT_FILES))) 时获取与其对应的输入文件(带路径)。

    当然,该规则仅在文件名唯一的情况下才有效。如果有人有更清洁的解决方案,请随时发布。

    【讨论】:

      【解决方案2】:

      在您的情况下,.SECONDEXPANSION: 有效,因为您可以使用 make 函数 (filter) 来计算每个输出文件的先决条件。在其他情况下,这可能是不可能的。但是还有另一个 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 将$(call MY_RULE,$(i)) 扩展为i = foo/bar/baz,并将结果视为常规make 语句。 $(eval ...) 的扩展没有其他作用,结果是空字符串。这就是为什么在我们的例子中$(foreach ...) 扩展为空字符串。但它做了一些事情:为每个输入文件动态创建新的 make 语句。

      • $(call NAME,PARAMETER) 扩展PARAMETER,将其分配给临时变量1,并在此上下文中扩展make 变量NAME。所以,$(call MY_RULE,$(i))i = foo/bar/baz 扩展为变量 MY_RULE$(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 解析和扩展添加的语句。这就是为什么您经常需要在宏定义中使用$$ 而不是$ 来转义这两个扩展中的第一个。

      而且它是如此强大,值得知道。

      【讨论】:

        【解决方案3】:

        在寻求帮助时,请提供一些实际的示例名称,以便我们更清楚地了解您所拥有的内容。它还有助于我们使用不会混淆的术语。

        我希望你真的想在你的食谱中使用$<,而不是$^

        如果您的“输入文件”是真正的输入文件(也就是说,它们本身不是由其他 make 规则生成的),那么您可以轻松解决这个问题with VPATH

        就用这个吧:

        VPATH := $(sort $(dir $(INPUT_FILES)))
        
        $(OUTPUT_DIR)/% : %
                program --input $< --output $@
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-30
          • 2011-03-23
          相关资源
          最近更新 更多