用 Tcl 作为 Makefile 中命令的解释器
Makefile 默认是用 /bin/sh
作为命令解释器的。
需要承认的是,对于稍微复杂一点的逻辑,Shell命令用起来并不是很顺手。
GNU Make 从 Version 3.64 (21 Apr 1993) 开始支持通过SHELL
变量来指定命令解释器。这给了我们用 Tcl 写 Makefile 的可能。
用Tcl作为Makefile命令解释器
SHELL = /usr/bin/tclsh
tets-tcl:
puts "Hello Tcl from Make"
通过SHELL
变量指定TCL解释器。
运行上面这个最简单的例子,会发现Tcl命令并没有实际执行,并且Tcl解释器不会退出。
究其原因,实际上是因为Tcl收到的命令等效于
/usr/bin/tclsh -c 'puts "Hello Tcl from Make"'
根据tclsh
程序的实现,由于第一个参数-
字符开头,tclsh 解释器认为没有要执行的脚本,而是把所有参数作为$::argv
的内容,等待从 stdin 读取Tcl命令。
解决办法自然是指定一个Tcl脚本。
可以取巧的是,tclsh默认会读取~/.tclshrc
文件作为启动脚本。因而我们可以在其中写入以下命令。
# File: ~/.tclshrc
if {[lindex $::argv 0] eq "-c"} {
eval [lindex $::argv 1]
exit
}
.ONESHELL
上面的方案有个问题,就是每一行Tcl命令都是在单独的Tcl解释器中执行的,因而无法写出需要多行命令的复杂逻辑。
GNU Make 版本 Version 3.82 (28 Jul 2010) 开始提供 .ONESHELL
这个特殊目标。因此,我们改成下面这样就可以了。
SHELL = /usr/bin/tclsh
.ONESHELL:
tets-tcl:
set hello 123
puts "hello = $$hello"
需要注意的是,这里的$
字符仍然需要写两个,以避免被当作Makefile的变量处理。
ONESHELL 的情况下,用于不显示当前命令的特殊字符@
或-
只需要加在第一行命令的前面。
.RECIPEPREFIX
Makefile默认用Tab字符(\t
)作为命令的前缀的。我们也许希望让用Tcl语言写的Makefile和默认的有点区分。
可以借助.RECIPEPREFIX
这个变量。类似于这样:
SHELL = /usr/bin/tclsh
.RECIPEPREFIX = %
.ONESHELL:
tets-tcl:
% puts "Hello Tcl from Make"
% puts "Hello Again"
.SHELLFLAGS
前面提到的额外传递给/usr/bin/tclsh
的参数-c
来自Makefile的追加。
从 GNU Make 版本 Version 3.82 (28 Jul 2010) 开始,可以通过 .SHELLFLAGS
变量进行修改。
比如我们可以写成
.SHELLFLAGS = -f tclmake.tcl
这使得我们可以根据项目的不同定制用于Makefile里Tcl命令处理模板。比如,把命令发送到某个服务端程序上执行。
附记
GNU Makefile 的版本变迁历史在源文件里的 NEWS
文件中获得。
GNU Make 官方网站:https://www.gnu.org/software/make/