正如您所指出的,无法测试$(shell ...)
输出是标准终端还是管道/文件。然而,我们可以做一些事情,每件事都有自己的优点/缺点。
make
在自定义脚本中检测 TTY
我们只需编写一个简单的 shell 脚本来拦截对 native 的调用make
,检测 TTY,并适当地定义一个变量。该解决方案的主要优点:
- 简单,
- 适用于
echo
等$(info ...)
命令$(shell ...)
,
- 配方无需修改,
- 每次回声都不会产生额外的进程,这在某些平台上可能会很慢(例如 Cygwin)。
ifdef IS_TTY
# DO NOT ADD TRAILING COMMENTS OR THIS WILL FAIL BECAUSE OF TRAILING SPACES
ESC := $(shell printf '\e')
R := $(ESC)[31m
Z := $(ESC)[0;0m
endif
$(info [info ] $Rred$Z black)
$(shell echo >&2 "[shell ] $Rred$Z black")
if_fancy:
@echo "[recipe] $Rred$Z black"
添加到路径中的自定义 make 脚本示例(例如/usr/local/bin/make
):
#! /bin/bash
[ -t 1 ] && IS_TTY=1 || IS_TTY=0
exec /usr/bin/make IS_TTY=$IS_TTY "$@"
此方法在所有这些情况下都提供了预期的输出
make # Colored
make >&2 # Colored
make | cat # NOT colored
make >tmp && cat tmp # NOT colored
在配方中检测并递归调用 Makefile
在某些情况下,可能无法拦截调用以进行调用或将其替换为自定义脚本。在此解决方案中,我们通过在第一遍中检测 TTY,然后使用适当的变量集递归调用相同的 Makefile 来解决该限制。
ifndef IS_TTY
.SILENT:
%:
@[ -t 1 ] && IS_TTY=1 || IS_TTY=0; $(MAKE) IS_TTY=$$IS_TTY "$@"
xyz:
@[ -t 1 ] && IS_TTY=1 || IS_TTY=0; $(MAKE) IS_TTY=$$IS_TTY
else
ifeq ($(IS_TTY),1)
# DO NOT ADD TRAILING COMMENTS OR THIS WILL FAIL BECAUSE OF TRAILING SPACES
ESC := $(shell printf '\e')
R := $(ESC)[31m
Z := $(ESC)[0;0m
endif
$(info [info ] $Rred$Z black)
$(shell echo >&2 "[shell ] $Rred$Z black")
recursive:
@echo "[recipe] $Rred$Z black"
endif
这种方法的缺点是它在 Makefile 中添加了相当多的垃圾。处理递归可能很棘手。在上面的例子中,我只应用了一些基本的递归机制,在make
有或没有目标的情况下应该可以工作,并且还应该传播变量。扩展这将是另一个问题的主题;-)
在每个回声处去除转义序列
我们在每个 echo 命令中测试输出是否是 TTY,如果不是,我们会去除转义序列(使用https://superuser.com/questions/380772/removing-ansi-color-codes-from-text-stream) . 为了减少在线上的垃圾邮件,我们使用了一个 make 变量$(STRIPESC)
。解决方案非常简单,但它只适用于echo
命令,并且在每次回显时都会产生一个额外的进程。它还需要使用回声编辑每个配方。
# DO NOT ADD TRAILING COMMENTS OR THIS WILL FAIL BECAUSE OF TRAILING SPACES
ESC := $(shell printf '\e')
R := $(ESC)[31m
Z := $(ESC)[0;0m
STRIPESC:=( [ -t 1 ] && cat || sed 's/\x1b\[[0-9;]*m//g' )
# $(info ...) not supported
# $(shell echo >&2 ...) not supported
if_tty:
@echo "[recipe] $Rred$Z black" | $(STRIPESC)
使用外部 echo 命令去除转义序列
这类似于上面的解决方案,只是语法更轻量级。优点/缺点是相同的。此外,在下面的示例中,我在 makefile 本身中生成了外部回显。在标准构建中,您将在某些标准路径中将此命令作为外部工具提供。
# DO NOT ADD TRAILING COMMENTS OR THIS WILL FAIL BECAUSE OF TRAILING SPACES
ESC := $(shell printf '\e')
R := $(ESC)[31m
Z := $(ESC)[0;0m
$(shell echo "#! /bin/bash" > echotty)
$(shell echo '[ -t 1 ] && echo "$$@" || echo "$$@" | sed "s/\x1b\[[0-9;]*m//g"' >> echotty)
$(shell chmod a+x echotty)
# $(info ...)
# $(shell echo >&2 ...)
.PHONY: echotty
echotty:
@./echotty "[recipe] $Rred$Z black"