3

我想编译一些 C++ 文件,我绝对必须将所有目标文件放在一个单独的构建目录中,但存储完全平坦,即没有任何进一步的子目录。我知道使用 VPATH 的常见解决方案,它是这样的:

SOURCES = foo/one.cpp \
    foo/bar/two.cpp \
    foo/bar/sub/three.cpp

OBJDIR = obj

VPATH=$(dir $(SOURCES))

OBJECTS = $(addprefix $(OBJDIR)/, $(notdir $(SOURCES:%.cpp=%.o)))

$(OBJDIR)/%.o : %.cpp
    @echo Should compile: $(filter %/$*.cpp, $(SOURCES))
    @echo Compiling $<

all: $(OBJECTS)

这个例子非常有效:我在“obj”子目录中获得了三个目标文件 one.o、two.o、three.o(您可以假设它只是存在)。

现在这是使用 VPATH 时的问题:如果碰巧有一个文件 'foo/three.cpp',那么它将被编译而不是在 SOURCES 变量中命名的 'foo/bar/sub/three.cpp'。不,我不能重命名任何一个文件;这个名字冲突只是存在,我对此无能为力。

所以我的问题是:如何告诉 Make使用出现在 SOURCES 变量中的“.cpp”文件?我认为最好的解决方案是在目标的先决条件中使用“过滤器”语句。我认为这应该可以使用二次扩展,但我不知道如何处理'%'。例如,我试过

 .SECONDEXPANSION:
$(OBJDIR)/%.o : $$(filter %/$$*.cpp, $(SOURCES))

但这不起作用。


更新:在 Tripleee 的帮助下,我设法使用以下方法完成了这项工作:

define make-deps
$(OBJDIR)/$(notdir $(1:%.cpp=%.o)): $1
endef

$(foreach d, $(SOURCES), $(eval $(call make-deps,$d)))

%.o : 
    @echo Should compile $^ into $@
    @echo Compiling $^
4

1 回答 1

3

我怀疑解决您的问题的最简单方法是明确摆脱VPATH并记录每个依赖项。这可以很容易地从您的SOURCES定义中获得;也许您想定义一个函数,但实际上归结为:

obj/one.o: foo/one.cpp
obj/two.o: foo/bar/two.cpp
obj/three.o: foo/bar/sub/three.cpp

实际规则可以保留,只是它不应该再包含内联依赖项,并且您可以跳过obj/子目录,因为它在每个依赖项中显式声明:

%.o : # Dependencies declared above
    @echo Should compile $^ into $@
    @echo Compiling $^

我将规则更改为使用$^,而不是$<在您拥有多个依赖项的情况下。对于您的情况,这可能是正确的或错误的;如果不是您需要的,请还原更改。

为了不需要手动维护依赖关系,您可能希望%.d为每个%.cpp文件生成。请参阅GNU Make 手册。(我试图通过使用 a 来做到这一点define,但似乎你不能用foreach循环声明依赖关系。)

针对评论中的问题,这不应以任何方式影响并行构建;当VPATH. 没有新的依赖关系,也没有新的规则。

于 2012-11-27T20:42:05.857 回答