GNU Make:在先决条件中使用过滤器功能

时间:2012-11-27 20:05:40

标签: makefile gnu-make

我想编译一些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',那么这个将被编译而不是'foo / bar / sub / 3。在SOURCES变量中命名的cpp'。不,我不能重命名任何一个文件;这个名字冲突只是存在,我对此无能为力。

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

 .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 $^

1 个答案:

答案 0 :(得分: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。见the GNU Make manual。 (我尝试使用define执行此操作,但似乎您无法使用foreach循环声明依赖项。)

在回应评论中的问题时,这不应以任何方式影响并行构建;它只是消除了当VPATH中有多个具有相同名称的biuld候选者时,原始Makefile不明确的依赖关系。没有新的依赖关系,也没有新的规则。