【问题标题】:How do I (efficiently) output a GNU Make 3.81 "dictionary" to a file我如何(有效地)将 GNU Make 3.81“字典”输出到文件
【发布时间】:2023-03-24 20:45:01
【问题描述】:

我有一个 GNU make 项目的“字典”。

MY_KEYS:=foo bar baz
foo_VALUE:=thing1
bar_VALUE:=thing2
baz_VALUE:=thing3

我希望将这个项目字典输出到一个文件中,使其看起来像这样......

foo=thing1
bar=thing2
baz=thing3

难度:“真正的”字典只有几 kb 大小,在 Windows 上已超出命令行限制。

我当前的低效实现最终生成了太多的 sh.exe 进程,并且最终需要将近 20 秒才能发出仅几 kb 的数据。

#Slow implementation
define NEWLINE


endef

list.txt:
    @printf '' > $@
    @$(foreach key,$(MY_KEYS), \
       printf '$(key)=$($(key)_VALUE)\n' >> $@ $(NEWLINE) )

$(file) 函数在 GNU make 3.81 中也不可用,因为那将是“显而易见的”修复。

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    您可以将键列表分割成适合命令行的子列表:

    slice?=2
    count?=3
    
    $(shell echo -n 'MY_KEYS:=' > a.txt)
    $(shell for i in `seq $(count)` ; do echo -n foo$$i' ' >> a.txt ; done)
    $(shell echo >> a.txt)
    $(shell for i in `seq $(count)` ; do echo foo$${i}_VALUE:=thing$$i >> a.txt ; done)
    
    include a.txt
    
    slices:=$(shell seq 1 $(slice) $(words $(MY_KEYS)))
    
    define NEWLINE
    
    
    endef
    
    list.txt:
            @echo -n > $@
            @$(foreach s, $(slices), $(foreach key, $(wordlist $(s), $(shell echo $$(( $(s)+$(slice)-1 )) ), $(MY_KEYS)), echo $(key)=$($(key)_VALUE) >> $@ ; ) ${NEWLINE})
    

    例如,在我的系统上,限制在 10K 到 100K 键之间:

    x@vb:/tmp$ rm -f list.txt ; time make slice=100000 count=100000 list.txt 
    make: execvp: /bin/sh: Argument list too long
    Makefile:19: recipe for target 'list.txt' failed
    make: *** [list.txt] Error 127
    
    real    0m4.366s
    user    0m1.452s
    sys 0m2.772s
    

    分割成 10K 子列表是可行的,而且速度仍然相当快:

    x@vb:/tmp$ rm -f list.txt ; time make slice=10000 count=100000 list.txt 
    
    real    0m10.034s
    user    0m3.852s
    sys 0m5.788s
    

    【讨论】:

      【解决方案2】:

      Make 通常会直接执行命令而不涉及 shell。 但是,配方中的那些 >> 重定向会导致 make 将配方传递给实际的 shell。 首先加速是避免配方中的shell元字符, 分叉数量减半。

      带有单个重定向的递归 make 适合这里的要求。 草图:

      .PHONY: dump
      dump:
          $(foreach _,${MY_KEYS},echo $_=${$__VALUE}${NEWLINE})
      
      list.txt:
          ${MAKE} -s dump >$@
      

      这将叉子的数量减半 c.f.天真的版本。

      dump 规则所做的只是回显某些内容。 但是 make 无论如何都会这样做 (实际上我们添加了-s 标志来阻止这种情况)。

      我们可以利用这一点并避免所有分叉。 -n 标志将使 make 进行回显, 并且不涉及外壳。

      .PHONY: dump
      dump:
          $(foreach _,${MY_KEYS},$_=${$__VALUE}${NEWLINE})
      
      list.txt:
          ${MAKE} -s -n dump >$@
      

      哇!

      但是,是的,升级你的 make。 这就是 20 世纪 ;-)

      【讨论】:

      • 那不需要我导出所有的键和值吗?导出它们不会将它们放入环境中,可能会引发资源枯竭吗?
      • @user13521 不,没有与 shell 的交互,根本没有。在最后一个公式中,我们要求 make 生成 dump。此时 make 需要生成 shell 命令,所以它扩展了配方。 $(foreach …) 扩展为所需的输出。请注意,这可能不是有效的 shell 语法。然而,由于我们使用-n 运行make,所以它永远不会传递给shell。呸。 Make 确实尽职尽责地回显了它会在标准输出上传递给 shell 的内容。任务完成。以毫秒为单位启动。
      猜你喜欢
      • 2016-10-23
      • 2023-04-09
      • 1970-01-01
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多