运行 makefile 中的命令并将 STDERR / STDOUT 打印到终端和文本文件

Run a command in a makefile and print STDERR / STDOUT to both the terminal and a text file

首先,我在 Windows10 上使用 GNU Make 4.3。我之前尝试过 GNU Make 4.2.1,结果与 4.3 完全相同。

无论如何,我有一个非常简单的 makefile,它只做(或至少打算做)一个简单的命令 运行 并将该命令的输出(stderr 和 stdout)打印到终端和文本文件。

$(info $$(MAKECMDGOALS) is "$(MAKECMDGOALS)". $$(SHELL) is \
"$(SHELL)". $$(MAKESHELL) is "$(MAKESHELL)". $$(COMSPEC) is "$(COMSPEC)". $$(OS) is "$(OS)".)

TEE := C:\tools\UnixTools\usr\local\wbin\tee.exe

LOG_FILE := C:\Temp\loggy__.txt

.PHONY : meep

all : meep

meep :
    $(info Making meep.)
    $(info Running command {dir 2>&1 | $(TEE) $(LOG_FILE)}.)
    $(shell dir 2>&1 | $(TEE) $(LOG_FILE))

最后一行给我带来了麻烦。有两件事 运行 与我的预期相反:

  1. 虽然 $(shell ...) 调用确实将 dir 命令的输出打印到我的文本文件和终端,但终端上的输出格式很奇怪。通常情况下,dir 每行打印一个元素,这里我将整个输出放在一行中,所以 GNU Make(或其他东西)似乎在显示之前以某种方式从输出中删除了换行符航站楼 window.

  2. 此外,我收到一条 The system cannot find the file specified. 错误消息(和往常一样,Windows 不够好,无法告诉我它找不到哪个文件). 运行 echo %errorlevel% 在同一个 CMD shell 中,我 运行 GNU Make 确认 Make 调用出错(退出状态为 2)。

奇怪的是,如果我直接在 CMD window 中 运行 命令 dir 2>&1 | C:\tools\UnixTools\usr\local\wbin\tee.exe C:\Temp\loggy__.txt,一切都完全按照预期运行,没有任何错误,所以我在想GNU Make 的 $(shell ...) 功能有问题,或者我使用错误。有没有人发现我尝试使用 $(shell ...) 函数的方式有些愚蠢?

我刚刚在我的 make 调用中添加了 --debug=a 以获得额外的调试输出,我在输出中发现了以下内容:

Creating temporary batch file C:\Users\mkemp\AppData\Local\Temp\make23400-1.bat
Batch file contents:
    @echo off
    dir 2>&1 | C:\tools\UnixTools\usr\local\wbin\tee.exe C:\Temp\loggy__.txt
CreateProcess(C:\Users\mkemp\AppData\Local\Temp\make23400-1.bat,C:\Users\mkemp\AppData\Local\Temp\make23400-1.bat,...)
Main thread handle = 00000000000000B4
Cleaning up temporary batch file C:\Users\mkemp\AppData\Local\Temp\make23400-1.bat
Creating temporary batch file C:\Users\mkemp\AppData\Local\Temp\make23400-2.bat
Batch file contents:
    @echo off
    Volume in drive C is Windows  Volume Serial Number is 045A-E422   Directory of C:\tools\UnixTools\usr\local\wbin (... the rest of the output) 
CreateProcess(C:\Users\mkemp\AppData\Local\Temp\make23400-2.bat,C:\Users\mkemp\AppData\Local\Temp\make23400-2.bat,...)

所以 GNU Make 的 $(shell ...) 函数似乎以某种方式将 dir 调用产生的输出解释为它需要 运行 的附加命令,当然这是无稽之谈。

使用 $(shell)在这里是废话。 make 完全按照您的指示行事。

正确的解决方案是不在没有意义的地方添加 $(shell ...) 函数调用。

meep :
    $(info Making meep.)
    $(info Running command {dir 2>&1 | $(TEE) $(LOG_FILE)}.)
    dir 2>&1 | $(TEE) $(LOG_FILE)

当然,在食谱中使用 $(info ...) 可能是假的。在每个食谱中,您 运行 正在 shell;使用 shell 的语法打印诊断消息。

meep:
    @echo Making meep. >&2
    @echo Running command '{dir 2>&1 | $(TEE) $(LOG_FILE)}.' >&2
    dir 2>&1 | $(TEE) $(LOG_FILE)

更好的是,不要 运行 make -s 并让 make 自己打印它是什么命令 运行ning,就像默认情况下那样(如果你不在你的 Makefile 中添加 @ 之前的所有命令,以使其更难调试)。