测试GNU make的eval函数

时间:2017-07-25 06:34:15

标签: makefile

GNU make手册说eval函数扩展了参数,然后提供扩展的结果来生成解析器。以下内容来自GNU make manual。

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 myprint)并删除'标签'回声前的字符。我在这个表单中编写makefile只是为了测试eval函数。

我的期望:首先,eval函数会扩展myprint,这是一个以' Tab'开头的echo命令,以及' Tab'用于使扩展文本成为合法配方。然后,eval将扩展文本提供给制造商解析器,制造商解析器将文本识别为配方,然后运行它。由于命令合法,makefile应该正常运行。

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

Makefile:6: *** recipe commences before first target.  Stop.

有人可以解释为什么会产生这样的错误吗?

4 个答案:

答案 0 :(得分:4)

  

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

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

define myprint
echo "this is a line"
endef

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

或:

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

goal:
    $(call myprint,line)
    gcc -o goal test.c

因为在make扩展之后,配方是有效的shell语法。但不是你写的,因为eval的扩展仍然被解释为make语法,而不是shell。为了说明evalcall的典型用法,请考虑以下事项:

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...函数调用,即:

  1. 展开$(eval...函数的参数($(call...函数):
    1. 展开$(call...函数(goaltest)的参数。在我们的案例中没有效果。
    2. 将结果分配给临时变量$(1)$(2)
    3. 在此上下文中展开mygoal变量,该变量将$(1)$(2)$$(call...替换为goaltest和{{1}分别。
  2. 将结果实例化(在内存中)作为make构造,在这种情况下是完整的规则:

    $(call...
  3. 第一阶段继续,但它对此实例化规则没有影响,因为配方在第二阶段由make扩展。

    在第二阶段,当构建goal: $(call myprint,line) gcc -o goal test.c 目标时,make会在执行之前扩展配方,即:

    1. 展开goal参数($(call myprint...,无效)。
    2. 将结果分配给临时参数line
    3. 在此上下文中展开变量$(1),生成:

      myprint
    4. 因此,所有这些都与我们编写规则相同:

      echo "this is a line"
      

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

        

      重要的是要意识到eval参数扩展了两次;   首先通过eval函数,然后是扩展的结果   当它们被解析为makefile语法时再次展开。这意味着你   可能需要为“$”字符提供额外的转义级别   使用eval。

答案 1 :(得分:3)

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

答案 2 :(得分:0)

我有一个关于你的例子的新问题,用于说明eval函数。

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))

我们知道mygoal首先通过调用扩展,然后通过eval扩展,然后通过make解析器扩展。我的问题是哪个函数扩展$$(call myprint,line) $(call myprint,line),调用函数($(call mygoal,goal,test))或eval函数。

我的观点是当$$(call myprint,line)调用mygoal而不是eval函数调用$(call mygoal,goal,test)时会扩展$$(call myprint,line)。我的理由是:因为eval函数将其输入视为makefile语法,并且mygoal$$(call myprint,line)的配方中,并且从GNU手册中我们知道make不会扩展配方,直到它是使用(配方被推迟),我们可以得出结论 Picasso.with(getApplicationContext()) .load(url_detail_img) .error(R.drawable.temp_img) .into(thumb_img); 没有扩展。

实际上,问题的关键点是eval和call函数如何扩展其输入。 eval函数是否扩展了配方(如果其输入是完整的makefile规则,其中包含目标,先决条件和配方)?

答案 3 :(得分:0)

@RenaudPacalet,我写了下面的makefile来测试是否扩展了call' eats'一美元。

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)正确输出"In my print $(ls)"时,必须首先将其扩展为echo "In my print $$(ls)",然后将其扩展为正确的shell命令echo "In my print $(ls)"。所以我认为通话功能并没有“吃掉”。一美元。

另一个证据是信息功能的输出。 GNU make手册说:

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

从手册中我们可以推断make会扩展info函数的参数。由于信息的输出为echo "In my print $(ls)",因此扩展前的参数应为echo "In my print $$(ls)"。因此,我们可以得出结论,呼叫功能并没有“吃”。一美元。