makefile - 带有生成的 C++ 源文件的 Makefile
问题描述
我正在使用一个工具来生成我需要包含在我的 C++ 项目编译中的 C++ 源文件。
为了非常简化事情,我的一个非生成源文件,main.cpp
如下:
extern void bar();
int main(int, char**)
{
bar();
return 0;
}
我Makefile
的在下面。整体结构是我们项目中已经存在的结构,我只是添加了生成所需源文件bar.cpp
和 makefile的部分bar.mk
,它添加bar.cpp
到SRCS
变量中,因此它(我希望)被包含在编译中:
SRCS := $(CURDIR)/main.cpp
#################### Adding everything between here... #####
BAR_MK := bar.mk
ifeq (,$(findstring clean,$(MAKECMDGOALS)))
include $(BAR_MK)
endif
#################### ...and here ###########################
OBJ_DIR := $(CURDIR)/obj
OBJS := $(patsubst %.cpp,%.o,$(subst $(CURDIR),$(OBJ_DIR),$(SRCS)))
a.out: $(OBJS)
@echo OBJS == $(OBJS)
@echo SRCS == $(SRCS)
$(CXX) $(OBJS)
#################### Adding everything between here... #####
GEN_DIR := $(CURDIR)/gen
# "Generate" my source file(s), add them to SRCS via an external makefile
$(BAR_MK): $(GEN_DIR)/bar.cpp
$(GEN_DIR)/bar.cpp:
mkdir -p $(dir $@)
echo "void bar () {}" > $@
echo SRCS += $@ > $(BAR_MK)
#################### ...and here ###########################
$(OBJ_DIR)/%.o: %.cpp
mkdir -p $(dir $@)
$(CXX) -c -o $@ $<
clean:
rm -rf $(OBJ_DIR)
rm -f a.out
rm -rf $(GEN_DIR)
rm -f $(BAR_MK)
make
问题是,由于“未定义对”的引用,我第一次运行时编译失败bar()
,但make
之后立即运行成功。这是因为在第一次运行时make
,当它评估a.out:
规则时,它只看到SRCS
包含main.cpp
,因此bar.cpp
不会被编译。然后在第二次(和后续)运行中,SRCS
同时包含main.cpp
and bar.cpp
,因此bar.cpp
在那时被编译。
这对我来说似乎很奇怪,因为根据“如何重新制作 Makefiles”的GNU Make 手册:
检查完所有 makefile 后,如果有任何实际更改,make 从一个干净的状态开始,并重新读取所有 makefile。
include bar.mk
因此,在我第一次调用GNU Make 处理后make
,发现它已过时并构建它,我希望它会从头开始,从头开始解析 Makefile,包括bar.mk
并且反过来SRCS
包含两者main.cpp
和bar.cpp
.
我的期望错了吗?或者我的(添加到)Makefile 有什么问题吗?我在 Linux 上使用 GNU Make 4.1。
解决方案
作为目标的规则bar.mk
没有配方。所以 make 认为没有办法生成缺失的bar.mk
. 但它不会立即失败,它会继续解析主 Makefile,生成bar.cpp
......bar.mk
但它无法提前知道这一点。结果是bar.mk
生成但不包括在内......并且 make 不会失败。不确定这是否应该被视为错误。我填写了一份可能是错误的报告。
无论如何,如果您明确告诉 make 如何构建bar.mk
它应该可以工作:
$(BAR_MK): $(GEN_DIR)/bar.cpp
echo 'SRCS += $<' > $@
$(GEN_DIR)/bar.cpp:
mkdir -p $(dir $@)
echo "void bar () {}" > $@
编辑:添加更多评论/替代解决方案
请注意,该$(BAR_MK)
规则仍然不完美:它声明了一个不是真实的先决条件。但在删除它之前,我们必须修复其他问题:您的模式规则与生成的源文件不匹配,因为您使用$(CURDIR)
它会预先设置完整路径。让我们摆脱它:
SRCS := main.cpp
BAR_MK := bar.mk
ifeq (,$(findstring clean,$(MAKECMDGOALS)))
include $(BAR_MK)
endif
OBJ_DIR := obj
OBJS := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(SRCS))
a.out: $(OBJS)
@echo OBJS == $(OBJS)
@echo SRCS == $(SRCS)
$(CXX) $(OBJS)
GEN_DIR := gen
$(BAR_MK):
echo 'SRCS += $(GEN_DIR)/bar.cpp' > $@
$(GEN_DIR)/bar.cpp:
mkdir -p $(dir $@)
echo "void bar () {}" > $@
$(OBJ_DIR)/%.o: %.cpp
mkdir -p $(dir $@)
$(CXX) -c -o $@ $<
clean:
rm -rf $(OBJ_DIR) a.out $(GEN_DIR) $(BAR_MK)
最后,如果您真的想生成两者bar.mk
并gen/bar.cpp
使用相同的规则,则有一种可能性,仍然告诉 make:具有多个目标的模式规则:
模式规则可能有多个目标。与普通规则不同,这不会像许多具有相同先决条件和配方的不同规则一样起作用。如果模式规则有多个目标,make 知道规则的配方负责生成所有目标。该配方仅执行一次以生成所有目标。在搜索与目标匹配的模式规则时,与需要规则的目标匹配的规则之外的规则的目标模式是偶然的:只需担心为当前有问题的文件提供配方和先决条件。但是,当运行此文件的配方时,其他目标将被标记为已自行更新。
在下面它将匹配模式规则的通配符a
:bar
PATTERN_TARGET := $(subst bar,b%r,$(BAR_MK) $(GEN_DIR)/bar.cpp)
$(PATTERN_TARGET):
mkdir -p $(GEN_DIR)
echo "void bar () {}" > $(GEN_DIR)/bar.cpp
echo 'SRCS += $(GEN_DIR)/bar.cpp' > bar.mk
但这可能太奇怪了,会完全破坏可维护性。更喜欢其他两个选项之一,它们更容易理解。
推荐阅读
- arrays - 排序数组 [QuickSort] 仍然比原生 Range.Sort 慢
- c++ - 洗牌的简单方法
- jsr352 - Jsr 352 - 无论状态如何都强制步骤
- sql - 需要根据 oracle 中的传入值更新列
- azure - 如何在 Azure 函数中获取 AccessToken 和声明
- mongodb - Mongo Mdbc powershell
- ios - 在“cordova prepare”之后,xCode 不断无法在我的手机上安装应用程序
- postgresql - Postgresql 条件排序由多种情况
- android - 片段分离/附加的 Viewpager 意外行为
- mysql - mysql选择值匹配存在或不存在的地方