【问题标题】:Run a command in a makefile and print STDERR / STDOUT to both the terminal and a text file在 makefile 中运行命令并将 STDERR / STDOUT 打印到终端和文本文件
【发布时间】:2020-05-12 19:54:43
【问题描述】:

首先,我在 Windows 10 上使用 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(或其他东西)以某种方式从输出中删除了换行符,然后才显示在终端中窗口。

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

奇怪的是,如果我直接在 CMD 窗口中运行命令 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 调用产生的输出解释为它需要运行的附加命令,这当然是无稽之谈。

【问题讨论】:

    标签: windows cmd makefile gnu-make


    【解决方案1】:

    使用 $(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 自己打印它正在运行的命令,就像默认情况下那样(如果你没有在你的Makefile 中撒上@ 在所有命令之前更难调试)。

    【讨论】:

    • 你知道吗,似乎 A) 你是对的 B) 我把我的问题简单化了。因为虽然在我的最小示例中删除 shell 调用解决了该示例的问题,但在我的实际问题中并没有。我想我需要做更多的调试。我仍然会接受你的回答,因为它在这个问题的上下文中是正确的。
    • 原来真正的罪魁祸首是我在目标配方调用的函数中使用的 $(eval undefine VAR_NAME)。我一直在使用 $(eval ...) 来定义和取消定义大量特定于目标的变量,因此我可以在目标的配方中进行一些命令式编程,并且在一个函数中我有一系列 $(eval undefine .. .)s 在我完成它们后删除这些变量。显然 GNU Make 不喜欢这样。 :(
    • 您必须清楚地记住的是,make 将在启动该配方的任何部分之前从整个配方的所有行中扩展所有变量和所有函数。因此,您不能在您希望在 调用配方中的 shell 操作之后运行的配方中使用 make 函数等。
    猜你喜欢
    • 2021-05-11
    • 2017-09-05
    • 2022-12-07
    • 2013-04-14
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多