最近在工作中,我一直在做一些从 Makefile 到替代构建系统的翻译。我在某些地方看到了一些使用功能映射、过滤器和 foreach 构造的非常复杂的 Make 代码。这让我感到惊讶,因为我认为构建脚本应该尽可能地具有声明性。
无论如何,这让我想到:Makefile 语言(比如最新的 GNU make 具体来说)图灵是否完整?
最近在工作中,我一直在做一些从 Makefile 到替代构建系统的翻译。我在某些地方看到了一些使用功能映射、过滤器和 foreach 构造的非常复杂的 Make 代码。这让我感到惊讶,因为我认为构建脚本应该尽可能地具有声明性。
无论如何,这让我想到:Makefile 语言(比如最新的 GNU make 具体来说)图灵是否完整?
是的,看到这个。一旦你有了 lambda,一切都会从那里走下坡路。
这是一个抄袭的斐波那契示例
这应该足以为更多的通用性奠定基础(我必须回去工作,否则我会玩更多。)
dec = $(patsubst .%,%,$1)
not = $(if $1,,.)
lteq = $(if $1,$(if $(findstring $1,$2),.,),.)
gteq = $(if $2,$(if $(findstring $2,$1),.,),.)
eq = $(and $(call lteq,$1,$2),$(call gteq,$1,$2))
lt = $(and $(call lteq,$1,$2),$(call not,$(call gteq,$1,$2)))
add = $1$2
sub = $(if $(call not,$2),$1,$(call sub,$(call dec,$1),$(call dec,$2)))
mul = $(if $(call not,$2),$2,$(call add,$1,$(call mul,$1,$(call dec,$2))))
fibo = $(if $(call lt,$1,..),$1,$(call add,$(call fibo,$(call dec,$1)),$(call fibo,$(call sub,$1,..))))
fact = $(if $(call lt,$1,..),.,$(call mul,$1,$(call fact,$(call dec,$1))))
numeral = $(words $(subst .,. ,$1))
go = $(or $(info $(call numeral,$(call mul,$1,$1)) $(call numeral,$(call fibo,$1)) $(call numeral,$(call fact,$1)) ),$(call go,.$1))
_ := $(call go,)
这会打印出正方形、斐波那契数和阶乘。数字大小似乎有 16 位限制。真可惜。
现在给出一个否定的答案:GNU make 积极地阻止了一些创建递归的机制:
1)递归扩展变量
在“递归函数”的意义上不是递归的:它们不能根据自身来定义:
Actually make detects the infinite loop and reports an error.
(顺便说一句,我看不出允许它们在实践中如何有用。)
2)规则链
也不能递归:
No single implicit rule can appear more than once in a chain. (...)
This constraint has the added benefit of preventing any infinite loop
in the search for an implicit rule chain.
(在调试我的 Makefile 时,我为此浪费了很多时间——除了所有其他使 makefile 难以维护的事情。)
P.S. for a recent project I wrote a patch to GNU make 3.82 which removes this limitation with a new -M option (see discussion). It works fine for me.