包含生成的 C++ 源文件的 Makefile
Makefile with generated C++ source files
我正在使用一个工具来生成我需要包含在我的 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)
问题是,由于"undefined reference to bar()
",我运行 make
第一次编译失败,但运行ning make
之后立即成功。这是因为在 make
的第一个 运行 上,当它计算 a.out:
规则时,它认为 SRCS
只包含 main.cpp
,因此 [=14] =] 没有被编译。然后在第二个(和随后的)运行s 中,SRCS
包含 main.cpp
和 bar.cpp
,因此 bar.cpp
在那个时候被编译。
这对我来说似乎很奇怪,因为根据 "How Makefiles Are Remade" 上的 GNU Make manual:
After all makefiles have been checked, if any have actually been changed, make starts with a clean slate and reads all the makefiles over again.
所以在 GNU Make 处理 include bar.mk
我的第一次 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:a pattern rule with multiple targets:
Pattern rules may have more than one target. Unlike normal rules, this
does not act as many different rules with the same prerequisites and
recipe. If a pattern rule has multiple targets, make knows that the
rule’s recipe is responsible for making all of the targets. The recipe
is executed only once to make all the targets. When searching for a
pattern rule to match a target, the target patterns of a rule other
than the one that matches the target in need of a rule are incidental:
make worries only about giving a recipe and prerequisites to the file
presently in question. However, when this file’s recipe is run, the
other targets are marked as having been updated themselves.
下面是 bar
的 a
匹配模式规则的通配符:
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
但这可能太奇怪了,会完全破坏可维护性。更喜欢其他两个选项中的一个,它们更容易理解。
我正在使用一个工具来生成我需要包含在我的 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)
问题是,由于"undefined reference to bar()
",我运行 make
第一次编译失败,但运行ning make
之后立即成功。这是因为在 make
的第一个 运行 上,当它计算 a.out:
规则时,它认为 SRCS
只包含 main.cpp
,因此 [=14] =] 没有被编译。然后在第二个(和随后的)运行s 中,SRCS
包含 main.cpp
和 bar.cpp
,因此 bar.cpp
在那个时候被编译。
这对我来说似乎很奇怪,因为根据 "How Makefiles Are Remade" 上的 GNU Make manual:
After all makefiles have been checked, if any have actually been changed, make starts with a clean slate and reads all the makefiles over again.
所以在 GNU Make 处理 include bar.mk
我的第一次 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:a pattern rule with multiple targets:
Pattern rules may have more than one target. Unlike normal rules, this does not act as many different rules with the same prerequisites and recipe. If a pattern rule has multiple targets, make knows that the rule’s recipe is responsible for making all of the targets. The recipe is executed only once to make all the targets. When searching for a pattern rule to match a target, the target patterns of a rule other than the one that matches the target in need of a rule are incidental: make worries only about giving a recipe and prerequisites to the file presently in question. However, when this file’s recipe is run, the other targets are marked as having been updated themselves.
下面是 bar
的 a
匹配模式规则的通配符:
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
但这可能太奇怪了,会完全破坏可维护性。更喜欢其他两个选项中的一个,它们更容易理解。