有什么办法可以告诉 make 在发生故障时打印出更好的诊断信息吗?

Is there any way to tell make to print better diagnostic information when a failure occurs?

GNU make 正在生成错误,我没有找到其根本原因。

一些简单的背景:该项目是一个嵌入式固件。我们正在 windows 主机上进行交叉编译。该构建实际上 运行s 在 cygwin 环境中。我已经构建该项目一段时间了,没有任何问题。这是一个关键点。项目构建正常,我没有对项目源或 Makefile 进行任何更改。

几天前我在我的cygwin环境中安装了python3。我 运行 Cygwin 设置和设置建议我更新十几个其他软件包已经有一段时间了。我没有多想就接受了更新建议。我没有捕获包列表或旧版本。我的意思是,会出什么问题?

这是出了什么问题:下次我构建这个项目时,make 失败了。错误是神秘的,根本原因在躲避我。我将 GNU make 从 4.3-1(新版本)降级回 4.2.1-2,但项目仍然无法正常构建。

这里是错误:

$ make -j8
compile message for file1.c
compile message for file2.c
...
compile message for fileN.c
make: *** [../../build/rules.mak:407: ../obj/fileN.o] Error 127

文档中未列出错误 127:https://www.gnu.org/software/make/manual/html_node/Error-Messages.html

搜索错误 127,我在 SO: 上发现了这个问题。从表面上看,问题看起来很相似,答案帮助我深入研究了问题。该答案中确定的根本原因是执行了与主机体系结构不匹配的命令。但是 (1) 我认为根本原因不同——请参阅下面的详细信息,并且 (2) 如果根本原因相同,none 的建议解决方案是该项目的可行选择。

这就是为什么我说根本原因难以捉摸:

  1. 如果我再次 运行 make -j8,make 将毫无问题地构建 fileN.c,但稍后在文件 X 上失败。我可以重复此过程几次直到一切都会构建,但这不是一个有效的答案,无论是对于人类还是对于自动构建。

  2. 如果我 运行 make clean 然后重新开始,make 将因不同的文件而失败,例如文件 N 将构建正常,但 make 将在文件 M 或 P 上失败。

  3. 如果我 运行 make -j1 只有一份工作,构建通常会成功而不会出错,但并非总是如此。

  4. make 调用的行号——最初——是一个带有多个命令的 make 宏,所以不清楚哪个命令可能是问题所在。所以我在配方中添加了一些诊断信息:which dos2unixfile -L dos2unixdos2unix --version 等等。现在,添加了这些诊断后,当 make 失败时,它有时会在使用 echo "dos2unix version:" 等简单命令的行上生成 127 错误。 echo怎么会导致错误127呢?为什么不执行前两次这个完全相同的 echo 命令?

我想提请注意最后一点,因为 make 在这里根本没有提供任何有用的诊断信息。

很明显错误不是执行了与主机架构不匹配的命令。 Make 一遍又一遍地执行相同的命令而没有错误。

很明显问题不在于任何特定的源文件,因为重新运行ning make 会产生不同的success/fail 配置文件。

这是一个失败的食谱示例。请记住,相同的配方在失败之前会成功数十次。而如果我make clean/make,在处理差异源文件时出现故障。

357 define GENERATE_DEPENDENCY
358   @dos2unix -q $(@:.o=.d)
359   @sed -e '/[a-zA-Z]:/ {' \
360       -e 's/\([a-zA-Z]\):[\\/]/\/cygdrive\/\L\//g' \
361       -e 's/\(.\)\\(.\)/\//g' \
362       -e '}' < $(@:.o=.d)  > $(@:.o=.dep)
363   @cp $(@:.o=.dep) $(@:.o=.dp2)
364   @sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\$$//' \
365       -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.dp2) >> $(@:.o=.d)
366   @rm $(@:.o=.dp2)
367
368 endef
...
404 $(COBJS) : $(OBJOUTPUTDIR)/%.o : %.c $(EVERYTHING_DEPENDS_ON) | $(OBJOUTPUTDIR)
405     $(COMPILEHOOK)
406     $(CC) $(LISTINGFLAG)=$(@:.o=.lst)  -c $(CPPINC) $(CPPFLAGS)  -MD $(call fixpath,$<) -o $@
407     $(GENERATE_DEPENDENCY)

看来根本不是项目的问题。我什至删除了项目并提取了在我的 cygwin 升级之前正确构建的项目的新副本。但这并没有解决问题。

我希望我可以回到早期安装的 cygwin,但我没有备份。我什至不知道升级了哪些包。

是否有任何命令行选项可以告诉 make 为故障打印更好的诊断信息,我将不胜感激。我尝试了“所有调试信息”选项:

$ make -j8 -d > make-output 2>&1

Make 生成了 4.8MB(86K 行)的输出,构建成功。但这和用一份工作建造一样慢,这是非常不希望的。使用 make -d 也会使实际构建问题的定位变得复杂。

或者,如果根本原因确实是体系结构不匹配,是否有某种方法可以让 make 识别错误发生时它试图执行的命令?就目前而言,我不知道哪个命令可能导致了问题,或者问题是否确实是体系结构不匹配。

问题是失败的不是 make。 Make 只是 运行 您提供给它的任何命令,并且这些命令以非 0 退出代码(在本例中为 127)退出。 Make 已经并且可以不知道 为什么您的命令以非 0 退出代码退出。 make 所能做的就是报告它收到的退出代码。

添加 -d 无济于事,因为它不是生成错误的原因。 -d 选项仅在以您不期望的方式构建(或未构建)某些内容时才有用。调试 recipes.

时完全没有帮助

Make 期望它调用的命令本身会生成足够的消息,说明它失败的原因,以便您能够弄清楚。如果它不这样做,那么你将不得不修复它:make 帮不上忙。

一个问题是,在所有食谱命令行前加上 @ 前缀是一种反模式。告诉 make 不要打印任何关于它正在做的事情就像试图蒙着眼睛调试你的代码一样。不要那样做。如果您希望大部分时间隐藏您的食谱,但仍然能够显示它们以进行调试,您可以尝试此处描述的方法:http://make.mad-scientist.net/managing-recipe-echoing/

问题看起来很明显,make 无法找到在 makefile 中指定的命令,因此会抛出 127 错误。
它无法在 cygwin bin 目录或指定的 PATH 中找到它。
在更新 cygwin 包时,可能会发生一些重要的实用程序未安装或新版本有一些依赖关系要么被遗漏了。
由于您确定的根本原因是在并行处理期间,最好是进行测试并传递
-O 选项以打印正确的 make 调用 [= parallel.This 选项中的 38=] 指示 make 保存它调用的命令的输出,并在命令完成后将其全部打印出来。

make -j8 -O

参考 link : https://www.gnu.org/software/make/manual/html_node/Parallel-Output.html

如果您觉得 echo 确实是一个简单测试代码的问题,那么做一个小测试会很好,我在其中并行编译文件并使用 ${NUMBER_OF_PROCESSORS} 这将占用系统的可用处理器。如果您愿意,可以将其更改为特定值。
我还添加了 .SILENT,因此我不需要为回显添加 @ 前缀。如果你想调试你可以评论它 #.SILENT :

.SILENT:
.PHONY:compile objs test
TARGET = program.exe
CC=gcc
INC = ./inc
SOURCES = file_1.c file_2.c file_3.c file_4.c file_5.c
OBJ_FILES:= $(SOURCES:.c=.o)


objs: $(OBJ_FILES)

%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

all: test 

# Enable parallel compilation
compile:
    make -j ${NUMBER_OF_PROCESSORS} -O objs

link : compile $(TARGET)

$(TARGET): $(OBJ_FILES)
    $(CC) $(CFLAGS) $(OBJ_FILES) -o $@

test: link 
    # check echo command
    echo "Executing test script"

执行使用:make test
最好测试一下这是否适合您,以及并行执行或 echo 命令是否确实存在问题。

编辑:
正如@matzeri 所建议的那样,通过禁用防病毒软件来测试一次会很好。