Makefile 测试GNU make的评估功能

Makefile 测试GNU make的评估功能,makefile,Makefile,GNU make手册说eval函数扩展参数,然后将扩展结果提供给make解析器。以下引用自GNU make手册 The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax. 我不太明白make解析器是如何处理eval提供的文本的,所以 我编写以下makefile进行测试 define myprint echo "this

GNU make手册说eval函数扩展参数,然后将扩展结果提供给make解析器。以下引用自GNU make手册

The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax. 
我不太明白make解析器是如何处理eval提供的文本的,所以 我编写以下makefile进行测试

define myprint
    echo "this is a line"
endef

goal:
    $(eval $(call myprint))
    gcc -o goal test.c
我知道正确调用
myprint
应该只使用call函数:
$(call myprint)
并在echo之前删除“Tab”字符。我以这种形式编写makefile只是为了测试eval函数

我的期望是:首先eval函数展开myprint,这是一个echo命令,前面有一个“Tab”,而“Tab”用于使展开的文本成为合法的配方。然后eval将扩展后的文本提供给maker解析器,后者将确定文本为菜谱,并运行它。由于该命令是合法的,因此makefile应该正常运行

但是,我遇到了这样一个错误:

Makefile:6: *** recipe commences before first target.  Stop.
有人能解释为什么make会产生这样的错误吗?

$(eval…
需要一个语法完整的makefile片段。它不能用于将标记粘贴到其他makefile构造中。也许手册没有足够清楚地解释这一点,但它是通过阅读其参数实现的,就像它是一个包含的makefile一样

扩展的结果被解析为makefile语法

您对
eval
的使用不同:您希望将其解析为shell语法。你可以写:

define myprint
echo "this is a line"
endef

goal:
    $(myprint)
    gcc -o goal test.c
或:

因为在进行扩展之后,配方是有效的shell语法。但不是您编写的,因为
eval
的扩展仍然被解释为make语法,而不是shell。为了说明一个典型的用法:<代码> EVA和<代码>调用< /代码>,考虑如下:

define myprint
echo "this is a $(1)"
endef

define mygoal
$(1):
    $$(call myprint,line)
    gcc -o $(1) $(2).c
endef
$(eval $(call mygoal,goal,test))
它比前两个示例(没有
eval
)要复杂一些,但它说明了
eval
的真正目的:以编程方式实例化make构造。下面是它的工作原理,一步一步:

在其2阶段算法的第一阶段期间,make展开
$(eval…
函数调用,即:

  • 展开
    $(eval…
    函数(
    $(调用…
    函数))的参数:
  • 展开
    $(调用…
    函数(
    goal
    test
    )的参数。在本例中无效
  • 将结果分配给临时变量
    $(1)
    $(2)
  • 在此上下文中展开
    mygoal
    变量,该变量分别替换
    $(1)
    $(2)
    $(调用…
    )中的
    goal
    test
    $(调用…
  • 将(内存中)结果实例化为make构造,在本例中为完整规则:

    goal:
        $(call myprint,line)
        gcc -o goal test.c
    
  • 第一阶段继续,但对该实例化规则没有影响,因为在第二阶段期间,配方由make展开

    在第二阶段,当需要构建
    目标时,make会在执行它之前展开配方,即:

  • 展开
    $(调用myprint…
    参数(
    ,无效)
  • 将结果分配给临时参数
    $(1)
  • 在此上下文中展开变量
    myprint
    ,它将生成:

    echo "this is a line"
    
  • 因此,所有这些都与我们编写的规则相同:

    goal:
        echo "this is a line"
        gcc -o goal test.c
    
    注意
    mygoal
    的初始定义中的双
    $

    重要的是要认识到eval参数被扩展了两次; 首先是eval函数,然后是扩展的结果 当它们被解析为makefile语法时,会再次展开 在以下情况下,可能需要为“$”字符提供额外级别的转义: 使用eval


    @RenaudPacalet,我编写以下makefile来测试调用的扩展是否会“吃掉”一美元

    define myprint
    echo "In my print $$(ls)"
    endef
    
    goal:
        $(call myprint)
        $(info $(call myprint))
        gcc -o goal test.c
    
    其产出是:

    echo "In my print $(ls)"
    echo "In my print $(ls)"
    In my print call.mk ... (files list)
    
    由于
    $(call myprint)
    正确地输出了
    “在我的打印$(ls)中”
    ,因此必须首先将其扩展为
    echo“在我的打印$(ls)中”
    ,然后将其扩展为正确的shell命令
    echo“在我的打印$(ls)”中”
    。因此我认为调用函数不会“吃掉”一美元

    define myprint
    echo "In my print $$(ls)"
    endef
    
    goal:
        $(call myprint)
        $(info $(call myprint))
        gcc -o goal test.c
    
    另一个证据是info函数的输出。GNU make手册说:

    $(info text…)
    This function does nothing more than print its (expanded) argument(s) to standard output. 
    

    从手册中我们可以推断make将扩展info函数的参数。由于信息的输出是my print$(ls)中的
    echo>,
    ,因此扩展前的参数应该是my print$$(ls)中的
    echo“
    。因此,我们可以得出这样的结论:调用函数不会“吃掉”一美元。

    感谢您的详细回复和示例。我想我明白了一个让我困惑的要点:在第一次扩展参数后,结果将按照常规make语法发送到make解析器。我认为前面带有“Tab”的echo命令不是常规语法。这就是这就是我的makefile无法正常运行的原因。我的理解正确吗?不,不完全正确。问题不在于制表符。请自己尝试,删除此前导制表符。您仍然会收到错误,因为最后,
    $(eval$(调用myprint))
    扩展为make语句,而它应该是纯shell。这与您将
    CFLAGS:=-g
    放在配方中的问题相同:在需要shell的地方生成语法。抱歉,我不知道如何比Florian已经编写的更好地解释这一点。请尝试再次阅读文档的这一部分,并思考一下:argu扩展eval函数的语法,然后将扩展的结果解析为makefile语法。扩展的结果可以定义新的make变量、目标、隐式或显式规则等。可能坚持使用makefile语法。很抱歉我的删除行为。我不熟悉如何在注释中编写代码。以下是我的before注释。是否继续你的意思是问题在于评估的源代码应该是make syntax?在你的建议之后,我试着遵循makefile,