【问题标题】:How do I process extremely long lists of files in a make recipe?如何处理制作配方中极长的文件列表?
【发布时间】:2011-08-12 12:04:09
【问题描述】:

因为GNU make 允许变量与内存一样大,所以构建大量依赖列表没有问题。但是,如果您想在配方中实际使用这些文件列表(用于构建目标的 shell 命令序列),您会遇到一个问题:该命令可能超过 shell 的 命令行长度限制,产生诸如“参数列表太长”之类的错误。

例如,假设我想连接列表$(INPUTS) 中包含的几个文件以生成一个文件combined.txt。通常,我可以使用:

combined.txt: $(INPUTS)
        cat $^ > $@

但是如果$(INPUTS) 包含数千个文件,就像我的情况一样,对cat 的调用太长并且失败。一般有没有办法解决这个问题?可以安全地假设存在一些与一个巨大命令具有相同行为的命令序列——在这种情况下,一系列cat 命令,每个输入文件一个,使用>> 附加到combined.txt会工作。但是如何说服make 生成这些命令呢?

【问题讨论】:

    标签: makefile


    【解决方案1】:

    在寻找答案时,我能找到的最佳建议是to break up the list into a series of smaller lists and process them using shell for loops。但是你不能总是这样做,即使你能做到,这也是一个混乱的黑客行为:例如,如何获得通常的make 命令失败后立即停止的行为并不明显。幸运的是,经过大量搜索和实验,事实证明确实存在通用解决方案。

    子shell和换行符

    make recipes 为配方中的每一行调用一个单独的子shell。这种行为可能令人讨厌且违反直觉:例如,一行中的cd 命令不会影响后续命令,因为它们在单独的子shell 中运行。然而,实际上我们需要让make 对很长的文件列表执行操作。

    通常,如果您使用常规变量赋值构建“多行”文件列表,使用反斜杠将语句分隔为多行,make 会删除所有换行符:

    # The following two statements are equivalent
    FILES := a b c
    
    FILES := \
    a \
    b \
    c
    

    但是,使用define 指令,可以构建包含换行符的变量值。更重要的是,如果您将这样的变量替换为配方,每一行确实将使用单独的子 shell 运行,例如,使用下面的 makefile 从 /home/jbloggs 运行 make test(并假设不存在名为 test 的文件) 将产生输出/home/jbloggs,因为cd .. 命令的效果在其子shell 结束时丢失:

    define CMDS
    cd ..
    pwd
    endef
    
    test:
            $(CMDS)
    

    如果我们使用define 创建一个包含换行符的变量,它可以像往常一样与其他文本连接,并使用所有常用的make 函数进行处理。这与$(foreach) 函数相结合,可以让我们得到我们想要的:

    # Just a single newline!  Note 2 blank lines are needed.
    define NL
    
    
    endef
    
    combined.txt: $(INPUTS)
            rm $@
            $(foreach f,$(INPUTS),cat $(f) >> $@$(NL))
    

    我们要求$(foreach) 将每个文件名转换为换行终止 命令,该命令将在其自己的子shell 中执行。对于更复杂的需求,您可以改为使用一系列echo 命令将文件名列表写入文件,然后使用xargs

    注意事项

    • define 指令被描述为可选地在第一行末尾使用=:=+= 标记来确定要创建哪个variable flavour——但请注意,只有适用于 GNU make 3.82 及更高版本!您很可能像我一样运行流行的版本 3.81,如果您添加其中一个标记,它会默默地为变量分配任何内容,这会导致很多挫败感。更多信息请参见here
    • 所有配方行必须以文字制表符开头,而不是我在这里使用的 8 个空格。

    【讨论】:

    • Ad last note:实际上,在 GNU Make 中重新定义前缀是可能的,所以你可以有以 8 个空格、4 个空格、>% 开头的食谱(我不知道)不记得我头顶上的确切咒语,但它在手册的某个地方)。
    猜你喜欢
    • 2017-11-12
    • 1970-01-01
    • 2018-08-29
    • 1970-01-01
    • 2012-03-23
    • 2010-11-15
    • 1970-01-01
    • 1970-01-01
    • 2017-07-11
    相关资源
    最近更新 更多