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 make
和 remake
,然后运行 remake -x
以了解 make
的作用和原因。
另外,GNU make 有很多内置规则。运行make -p
打印它们,然后改进你的Makefile
以利用它们。例如,您应该更喜欢$(COMPILE.c)
而不是$(CC)
如果您使用GCC 的一些最新变体,您希望在调用gcc
或g++
时通过至少传递-Wall -Wextra
来启用编译警告。如果您使用GDB 进行调试,您还想通过-g
。
最后,最近的GCC 编译器有很多 有趣的选项。所以请阅读Invoking GCC。如果您的 C++ 编译器是 Clang 或 MinGW,请花时间阅读其文档。
如果在 Linux 上,您可能会使用 binutils(或者可能是 gold)中的链接器,并且由 gcc
或 g++
在您的 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 在没有任何消息的情况下停止的主要内容,如果未能解决你的问题,请参考以下文章
Google Cloud Functions 在没有错误消息的情况下崩溃
Google Sheet Script:有没有办法在不停止脚本的情况下显示消息?