Makefile 在没有任何消息的情况下停止

Posted

技术标签:

【中文标题】Makefile 在没有任何消息的情况下停止【英文标题】:Makefile stopped without any messages 【发布时间】:2020-07-17 10:56:56 【问题描述】:

我尝试编译一些 .cpp 文件。使用这个 makefile 代码

.PHONY: all

define CompileModule
$(2): $(1)
    $(call __verify_path,$(TMP_DIR))
    @echo Compile $(1) to $(2)
    $(CC) -c -o $(2) $(1)
endef

$(foreach module,$(ALL_CPP), $(eval $(call CompileModule, $(module), $(addprefix $(TMP_DIR)\, $(addsuffix .o, $(basename $(notdir $(module))))))))

all: | setup $(OUTPUT_FULL_NAME) clean

setup:
    @echo Setup...
    $(call __verify_path,$(BIN_DIR))
    $(call __verify_path,$(TMP_DIR))
    $(call __verify_path,$(LIB_DIR))
    @echo Setup finished...

clean: 
    @echo Clean...
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.o))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.res))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.cof))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.c))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.h))
    $(call __delete_path,$(call __trans,$(TMP_DIR)))
    @echo Clean finished...

$(OUTPUT_FULL_NAME): $(ALL_OBJ) $(RPC_T_MODULE_OBJ)
    @echo Link objects... >&2
    $(call __verify_path,$(LIB_DIR))
    $(LD) $^ -o $@ $(call __trans,$(L_ATLANTIS)/LIB/crtexe.o) --allow-multiple-definition $(LINK_OPTION) --start-group $(LIB_MODULE_PARAM) --end-group
    $(__Strip_DebugInfo__)

$(RPC_T_MODULE) : $(RPC_T_IDL_MODULES)
    @echo Run MIDL... >&2
    $(MIDL) $(MIDL_OPT) $^

$(RPC_T_MODULE_OBJ): $(RPC_T_MODULE)
    @echo Compile IDL module... >&2
    $(CC) -c -x c++ -fpermissive -o $@ $^

成功了,但只编译列表中的第一个文件。

好的,我尝试使用此站点的代码(当然,有一些更改):

MakeTarget=$(addprefix $(TMP_DIR)\, $(addsuffix .o, $(basename $(notdir $1))))
obj.cpp=
define obj
$(call MakeTarget, $1) : $1
obj$(suffix $1) += $(call MakeTarget, $1)
$(info $1 to $(call MakeTarget, $1))
endef

define SOURCES
$(foreach src,$1,$(eval $(call obj,$src)))
$(info Foreach finished...)
endef

$(eval $(call SOURCES, $(ALL_CPP)))

all : $(OUTPUT_FULL_NAME) clean

clean: 
    $(info Clean...)
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.o))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.res))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.cof))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.c))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.h))
    $(call __delete_path,$(call __trans,$(TMP_DIR)))
    $(info Clean finished...)

$obj.cpp : % :
    $(info $obj.cpp)
    $(CC) -c -o $@ $^

$(OUTPUT_FULL_NAME): $obj.cpp $(RPC_T_MODULE_OBJ)
    $(info  Link objects... )
    $(call __verify_path,$(LIB_DIR))
    $(LD) $^ -o $@ $(call __trans,$(L_ATLANTIS)/LIB/crtexe.o) --allow-multiple-definition $(LINK_OPTION) --start-group $(LIB_MODULE_PARAM) --end-group
    $(__Strip_DebugInfo__)

$(RPC_T_MODULE) : $(RPC_T_IDL_MODULES)
    $(info  Run MIDL... )
    $(MIDL) $(MIDL_OPT) $^

$(RPC_T_MODULE_OBJ): $(RPC_T_MODULE)
    $(info  Compile IDL module... )
    $(CC) -c -x c++ -fpermissive -o $@ $^

.PHONY: all

同样的结果。

在输出中,我看到带有目标对象的文件列表(右)、“Foreach 完成”、来自 $obj.cpp 的目标列表(再次,正确)、用于编译第一个模块的 G++ 字符串(再次,正确) - 仅此而已。在目标目录中,我找到了第一个目标文件。

看起来 Make 在编译第一个文件后立即停止,没有任何消息。

怎么了?

【问题讨论】:

在第一个视图中,您似乎为每个文件运行内部 make 子例程,这不是 makefile 通常的工作方式。 “通常”一个生成文件包含规则和先决条件,它只是运行每个规则集的接收。内部函数“$(call...) 或通常用于从某些参数生成通用的东西,但不是任何具体对象/源/目标的一部分。 什么操作系统,什么编译器?请在您的问题中显示一些minimal reproducible example。如果没有更多详细信息,您的问题就不清楚了。 向我们展示展示您的问题的 C++ 代码。我们猜不出来。 【参考方案1】:

欢迎来到 Stack Overflow。你给我们的信息不完整,所以我必须猜测一些要点。

Make 必须决定执行哪些规则;如果您没有指定目标,Make 将运行默认规则,这是 makefile 中的第一条规则(除非您另外指定)。在您的第一个示例中,我没有看到 all 的规则,但您的 foreach 创建了多个规则,因此 Make 运行其中的第一个,这是构建列表中第一个目标文件的规则。

要解决这个问题,请为all 编写规则,并为其提供目标文件列表作为先决条件。

你的第二个例子非常复杂——而且不完整——但这里有一个非常严重的问题:

$obj.cpp : % :
    $(info $obj.cpp)
    $(CC) -c -o $@ $^

您可能打算将其设为static pattern rule,但前提条件列表为空;第二个冒号右侧没有任何内容。所以$^ 扩展为空,编译器命令将失败。

更根本的是,您忽略了工程的一条基本规则:从小而简单的完美工作开始,然后逐步建立。如果你建造一台大型新机器,使用一种奇怪的新技术,一步到位,它肯定会失败。

编辑: 在您的第二个示例中,您似乎遇到了与第一个示例类似的问题。您使用模板创建了多个规则,但没有指定目标,因此这些规则中的第一个成为默认规则。 Make 永远不会运行 all 规则。

要解决此问题,请将all 规则放在生成obj 规则的循环上方。

【讨论】:

我从那里获得第二个示例的代码:***.com/questions/27942565/… 我用关于第一个示例的更多信息更改了我的帖子。情况类似:第一个文件编译然后全部停止。 @СергейИванов:我的答案保持不变。如果您没有指定目标(例如make all),则 Make 将运行默认目标,这(通常)是第一条规则的目标。第一个示例中的第一条规则是foreach 生成的规则之一。【参考方案2】:

感谢您的帮助。此代码正常工作:

.PHONY: all

all: | setup $(OUTPUT_FULL_NAME) clean

ALL_OBJ=
define CompileModule
ALL_OBJ+= $(2)
$(2): $(1)
    $(call __verify_path,$(TMP_DIR))
    @echo Compile $(1) to $(2)
    $(CC) -c -o $(2) $(1)

endef

$(foreach module,$(ALL_CPP), $(eval $(call CompileModule, $(module), $(addprefix $(TMP_DIR)\, $(addsuffix .o, $(basename $(notdir $(module))))))))

setup:
    @echo Setup...
    $(call __verify_path,$(BIN_DIR))
    $(call __verify_path,$(TMP_DIR))
    $(call __verify_path,$(LIB_DIR))
    @echo Setup finished...

clean: 
    @echo Clean...
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.o))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.res))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.cof))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.c))
    $(call __delete_file,$(call __trans,$(TMP_DIR)/*.h))
    $(call __delete_path,$(call __trans,$(TMP_DIR)))
    @echo Clean finished...

$(ALL_OBJ):

$(OUTPUT_FULL_NAME): $(ALL_OBJ) $(RPC_T_MODULE_OBJ)
    @echo Link objects... >&2
    $(call __verify_path,$(LIB_DIR))
    $(LD) $^ -o $@ $(call __trans,$(L_ATLANTIS)/LIB/crtexe.o) --allow-multiple-definition $(LINK_OPTION) --start-group $(LIB_MODULE_PARAM) --end-group
    $(__Strip_DebugInfo__)

$(RPC_T_MODULE) : $(RPC_T_IDL_MODULES)
    @echo Run MIDL... >&2
    $(MIDL) $(MIDL_OPT) $^

$(RPC_T_MODULE_OBJ): $(RPC_T_MODULE)
    @echo Compile IDL module... >&2
    $(CC) -c -x c++ -fpermissive -o $@ $^

【讨论】:

【参考方案3】:

一个很好的开源工具来调试Makefile-s(那些为GNU make写的)是remake(你会运行它@987654346 @)

您可以从它们的开源代码编译 GNU makeremake,然后运行 ​​remake -x 以了解 make 的作用和原因。

另外,GNU make 有很多内置规则。运行make -p 打印它们,然后改进你的Makefile 以利用它们。例如,您应该更喜欢$(COMPILE.c) 而不是$(CC)

如果您使用GCC 的一些最新变体,您希望在调用gccg++ 时通过至少传递-Wall -Wextra 来启用编译警告。如果您使用GDB 进行调试,您还想通过-g

最后,最近的GCC 编译器有很多 有趣的选项。所以请阅读Invoking GCC。如果您的 C++ 编译器是 Clang 或 MinGW,请花时间阅读其文档。

如果在 Linux 上,您可能会使用 binutils(或者可能是 gold)中的链接器,并且由 gccg++ 在您的 Makefile 中间接调用的链接器有许多有趣的选项。

如果您为 Linux 编程,请阅读 Advanced Linux Programming,然后阅读 syscalls(2)。

在某些情况下,您可能会考虑替代开源 build automation 工具,例如 ninja,并且您可能(对于大型或遗留项目)考虑将它们组合起来(例如,从您的 Makefile 内部运行 ninja) .

在某些应用程序中,您可能希望使用ANTLR 或SWIG 等工具生成一些C++ 代码。

请注意,GNU make 的变体可以使用 GNU guile 编写脚本或使用 plugins 进行扩展。 GNU autoconf 和 GNU automake 可以(部分)生成你的 Makefile

当然看看github或gitlab上的现有开源项目寻找灵感


你的代码有

$(call __delete_file,$(call __trans,$(TMP_DIR)/*.o))

但是你为什么不像大多数开源项目那样使用$(RM)呢?

【讨论】:

以上是关于Makefile 在没有任何消息的情况下停止的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有错误消息的情况下中止makefile

Google Cloud Functions 在没有错误消息的情况下崩溃

Google Sheet Script:有没有办法在不停止脚本的情况下显示消息?

在没有启用咨询支持的情况下,代理的 activemq 静态网络是不是会停止转发消息?

如何在makefile中使用cflow?

Tomcat 在没有任何日志或任何堆栈的情况下停止