如何将对象文件放置在单独的子目录中

我尝试使用make将目标文件放置在单独的子目录中,可能是一个非常基本的技术。 我试图使用此页面中的信息: http : //www.gnu.org/software/hello/manual/make/Prerequisite-Types.html#Prerequisite-Types

我从make得到以下输出:make:***没有规则来做目标ku.h', needed by obj / kumain.o ku.h', needed by '。 停止。

然而,ku.h是一个不依赖于目标的依赖项(虽然它明显包含在c源文件中)。 当我不尝试使用目录文件的子目录(即错过了OBJDIR部分),它工作正常。 为什么认为ku.h是一个目标?

我的makefile是这样的:(风格是在阅读各种信息源之后)

 .SUFFIXES: .SUFFIXES: .c .o CC=gcc CPPFLAGS=-Wall LDLIBS=-lhpdf VPATH=%.c src VPATH=%.h src VPATH=%.o obj OBJDIR=obj objects= $(addprefix $(OBJDIR)/, kumain.o kudlx.o kusolvesk.o kugetpuz.o kuutils.o \ kurand.o kuASCboard.o kuPDFs.o kupuzstrings.o kugensud.o \ kushapes.o ) ku : $(objects) $(CC) $(CPPFLAGS) -o ku $(objects) $(LDLIBS) $(objects) : ku.h kudefines.h kuglobals.h kufns.h | $(OBJDIR) $(OBJDIR): mkdir $(OBJDIR) .PHONY: clean clean : rm $(objects) 

编辑:我应用更改使用vpath指令。 我的版本是VPATH = xxx和vpath%.c xxx的混合。 但是现在我又遇到了另一个问题(这是我添加错误的vpath之前的原始问题)。 这是现在的输出:

  gcc -o ku -lhpdf obj/kumain.o obj/kudlx.o obj/kusolvesk.o ..etc gcc: obj/kumain.o: No such file or directory gcc: obj/kudlx.o: No such file or directory gcc: obj/kusolvesk.o: No such file or directory gcc: obj/kugetpuz.o: No such file or directory gcc: obj/kuutils.o: No such file or directory gcc: obj/kurand.o: No such file or directory gcc: obj/kuASCboard.o: No such file or directory gcc: obj/kuPDFs.o: No such file or directory gcc: obj/kupuzstrings.o: No such file or directory gcc: obj/kugensud.o: No such file or directory gcc: obj/kushapes.o: No such file or directory make: *** [ku] Error 1 

看起来,make并没有对目标文件应用隐式规则,尽pipe手册中提到“隐式规则告诉如何使用惯用的技术,这样当你想使用它们的时候,你不必详细地指定它们。是C编译的一个隐式规则,文件名决定运行哪个隐式规则,例如C编译通常需要一个.c文件,然后生成一个.o文件,因此当C编译出现这个组合文件名结尾“。 还有“在VPATH或vpath中指定的目录中进行search也会在考虑隐式规则时发生(请参阅使用隐式规则)”。

再次在这里“例如,当一个文件foo.o没有明确的规则时,make会考虑隐式的规则,比如内置的规则来编译foo.c,如果这个文件存在的话。如果这个文件在当前目录中没有,如果在任何目录中foo.c存在(或在makefile中提及),则应用隐含的C编译规则。

任何协助获取隐式规则为我的生成文件工作将不胜感激。

编辑否2:感谢杰克·凯利我已经做出明确的规则来编译.c文件,因为我无法在任何地方尝试使用隐式规则。 也感谢al_miro的vpath信息。

这是工作文件:

 .SUFFIXES: .SUFFIXES: .c .o CC=gcc CPPFLAGS=-Wall LDLIBS=-lhpdf OBJDIR=obj vpath %.c src vpath %.h src objects = $(addprefix $(OBJDIR)/, kumain.o kudlx.o kusolvesk.o kugetpuz.o kuutils.o \ kurand.o kuASCboard.o kuPDFs.o kupuzstrings.o kugensud.o \ kushapes.o ) ku : $(objects) $(CC) $(CPPFLAGS) -o ku $(objects) $(LDLIBS) $(OBJDIR) obj/%.o : %.c ku.h kudefines.h kuglobals.h kufns.h $(CC) -c $(CPPFLAGS) $< -o $@ .PHONY : clean clean : rm $(objects) 

由于您使用的是GNUmake,所以使用模式规则来编译目标文件:

 $(OBJDIR)/%.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 

这是我用于大多数项目的makefile,

它允许把源文件,头文件和内联文件放在子文件夹中,子文件夹的子文件夹等等,并且会自动为每个对象生成一个依赖文件。这意味着修改头文件和内联文件将触发相关文件的重新编译。

源文件是通过shell查找命令检测到的,所以不需要明确指定,只是不断的编码到你的心中。

当项目编译时,它也会将所有文件从“资源”文件夹复制到bin文件夹中,大部分时间我都很喜欢。

为了提供应有的功劳,自动依赖function基本上是基于Scott McPeak的页面,可以在这里find,还有一些额外的修改/调整,以满足我的需求。

示例Makefile

 #Compiler and Linker CC := g++-mp-4.7 #The Target Binary Program TARGET := program #The Directories, Source, Includes, Objects, Binary and Resources SRCDIR := src INCDIR := inc BUILDDIR := obj TARGETDIR := bin RESDIR := res SRCEXT := cpp DEPEXT := d OBJEXT := o #Flags, Libraries and Includes CFLAGS := -fopenmp -Wall -O3 -g LIB := -fopenmp -lm -larmadillo INC := -I$(INCDIR) -I/usr/local/include INCDEP := -I$(INCDIR) #--------------------------------------------------------------------------------- #DO NOT EDIT BELOW THIS LINE #--------------------------------------------------------------------------------- SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT))) #Defauilt Make all: resources $(TARGET) #Remake remake: cleaner all #Copy Resources from Resources Directory to Target Directory resources: directories @cp $(RESDIR)/* $(TARGETDIR)/ #Make the Directories directories: @mkdir -p $(TARGETDIR) @mkdir -p $(BUILDDIR) #Clean only Objecst clean: @$(RM) -rf $(BUILDDIR) #Full Clean, Objects and Binaries cleaner: clean @$(RM) -rf $(TARGETDIR) #Pull in dependency info for *existing* .o files -include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT)) #Link $(TARGET): $(OBJECTS) $(CC) -o $(TARGETDIR)/$(TARGET) $^ $(LIB) #Compile $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) @mkdir -p $(dir $@) $(CC) $(CFLAGS) $(INC) -c -o $@ $< @$(CC) $(CFLAGS) $(INCDEP) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT) @cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp @sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT) @sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT) @rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp #Non-File Targets .PHONY: all remake clean cleaner resources 

VPATH行是错误的,他们应该是

 vpath %.c src vpath %.h src 

即不是大写,没有=。 现在,它没有find.h文件,并认为它是一个目标。

通常,您必须在将文件放置在$(OBJDIR)的所有规则的左侧指定$(OBJDIR) $(OBJDIR) ,或者可以从$(OBJDIR)运行make。 VPATH用于源,不用于对象。

看看这两个链接更多的解释,和一个“聪明”的解决方法。

下面的解决scheme在我看来并不好,因为我非常喜欢内置的规则。 但是,GNU make不支持vpath等输出目录。 而内置的规则不能匹配,因为%.o中的%.o将匹配obj/foo.o obj/foo ,而使用vpath %.c src/ for src/obj/foo.c ,但不是src/foo.c

但是,这与您所能获得的内置规则非常接近,因此我所知道的是最好的解决scheme。

 $(OBJDIR)/%.o: %.c $(COMPILE.c) $(OUTPUT_OPTION) $< 

说明: $(COMPILE.c) $(OUTPUT_OPTION) $<实际是如何实现.co ,请参阅http://git.savannah.gnu.org/cgit/make.git/tree/default.c (它甚至是在手册中提到)

此外,如果$(OBJDIR)只包含自动生成的文件,您可以使用订单唯一的先决条件即时创build它,使清理规则稍微简单一些:

 $(OBJDIR): mkdir -p $(OBJDIR) $(OBJDIR)/%.o: %.c | $(OBJDIR) $(COMPILE.c) $(OUTPUT_OPTION) $< .PHONY: clean clean: $(RM) -r $(OBJDIR) 

这要求只有订单function可用,您可以使用$(filter order-only, $(.FETAURES)) 。 我已经检查了Kubuntu 14.04 GNU make 3.81和OpenSUSE 13.1 GNU make 3.82。 两者都是以订单启用的方式构build的,现在让我们感到困惑的是,为什么Kubuntu 14.04自带了比OpenSUSE 13.1更早版本的GNU make。 不pipe怎样,现在要下载make 4.1了:)

对于那些使用隐式规则(和GNU MAKE)的人来说。 这是一个简单的makefile,它支持不同的目录:

 #Start of the makefile VPATH = ./src:./header:./objects OUTPUT_OPTION = -o objects/$@ CXXFLAGS += -Wall -g -I./header Target = $(notdir $(CURDIR)).exe Objects := $(notdir $(patsubst %.cpp,%.o,$(wildcard src/*.cpp))) all: $(Target) $(Target): $(Objects) $(CXX) $(CXXFLAGS) -o $(Target) $(addprefix objects/,$(Objects)) #Beware of -f. It skips any confirmation/errors (eg file does not exist) .PHONY: clean clean: rm -f $(addprefix objects/,$(Objects)) $(Target) 

让我们仔细看看(我会用curdir来指代当前目录):

这一行用于获取curdir / src中使用的.o文件的列表。

 Objects := $(notdir $(patsubst %.cpp,%.o,$(wildcard src/*.cpp))) #expands to "foo.o myfoo.o otherfoo.o" 

通过variables将输出设置为不同的目录(curdir / objects)。

 OUTPUT_OPTION = -o objects/$@ #OUTPUT_OPTION will insert the -o flag into the implicit rules 

为了确保编译器find新对象文件夹中的对象,将path添加到文件名。

 $(Target): $(Objects) $(CXX) $(CXXFLAGS) -o $(Target) $(addprefix objects/,$(Objects)) # ^^^^^^^^^^^^^^^^^^^^ 

这只是一个例子,有很大的改进空间。

有关更多信息,请参阅: 制作文档。 见10.2章节

或者: Oracle:编程实用程序指南