我尝试编译一些。cpp文件。 使用此生成文件代码
.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 complete”,来自${obj.cpp}的目标列表(再次,右),用于编译第一个模块的g++字符串(再次,右)--仅此而已。 在目标目录中,我找到了第一个目标文件。
看起来Make在编译第一个文件后立即停止,没有任何消息。
什么错了?
欢迎使用堆栈溢出。 你给了我们不完整的信息,所以我必须猜测一些要点。
Make必须决定执行哪些规则; 如果您没有指定任何目标,Make将运行默认规则,这是makefile中的第一个规则(除非您另有指定)。 在您的第一个示例中,我没有看到all
的规则,但是您的foreach
创建了几个规则,因此Make运行其中的第一个规则,这是构建列表中第一个对象文件的规则。
要解决这个问题,请为all
编写一个规则,并将对象文件列表作为先决条件提供给它。
您的第二个例子非常复杂--而且不完整--但是这里有一个非常严重的问题:
${obj.cpp} : % :
$(info ${obj.cpp})
$(CC) -c -o $@ $^
您可能希望这是一个静态模式规则,但先决条件列表是空的; 第二个冒号右边没有任何东西。 因此$^
将扩展为nothing,编译器命令将失败。
更根本的是,你忽略了工程学的一个基本规则:从一些小而简单,工作完美的东西开始,然后建立起来。 如果你造了一台大型的新机器,使用一种奇怪的新技术,一步到位,它肯定会失败。
编辑:在您的第二个示例中,看起来您有一个类似于第一个示例的问题。 您使用模板创建了几个规则,但没有指定目标,因此这些规则中的第一个成为默认规则。 Make从不运行全部
规则。
要解决这个问题,请将all
规则置于生成obj
规则的循环之上。
用于调试makefile
-s(为GNU make编写的)的一个很好的开源工具是remake(您将以remake-x
)
您可以从其开源代码编译GNUmake
和remake
,然后运行remake-x
来了解make
正在做什么以及为什么要做。
此外,GNU make有很多内置规则。 运行make-p
以打印它们,然后改进您的makefile
以利用它们。 例如,您应该首选$(compile.c)
而不是$(CC)
如果使用GCC的某些最新变体,则需要在调用GCC
或g++
时通过至少传递-wall-wextra
来启用编译警告。 如果使用GDB进行调试,您还希望传递-g
。
最后,最近的GCC编译器有很多有趣的选项。 所以请阅读有关调用gcc的内容。 如果您的C++编译器是Clang或MinGW,请花时间阅读它的文档。
如果在Linux上,您可能会使用binutils(或者gold)内部的链接器,而该链接器(由gcc
或g++
在您的makefile
中间接调用)有许多有趣的选项。
如果您为Linux编程,请阅读Advanced Linux Programming,然后阅读syscalls(2)。
在某些情况下,您可以考虑其他开源构建自动化工具,例如ninja,并且(对于大型或遗留项目)可以考虑将它们组合起来(例如,从makefile
内部运行ninja
)。
在某些应用程序中,您可能希望使用ANTLR或SWIG等工具生成一些C++代码。
请注意,GNU make的一个变体是可通过GNU guile编写脚本的,或者是可通过插件扩展的。 GNU autoconf和GNU automake可以(部分)生成makefile
当然,从github或gitlab上现有的开源项目中寻找灵感。
您的代码具有
$(call __delete_file,$(call __trans,$(TMP_DIR)/*.o))
但是为什么不像大多数开源项目那样使用$(RM)
呢?
谢谢你的帮助。 这段代码工作正常:
.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 $@ $^