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

时间:2011-03-03 08:10:13

标签: makefile gnu-make

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

我从make获得以下输出:

make: *** No rule to make target `ku.h', needed by `obj/kumain.o'.  Stop.

然而,ku.h是一个依赖而不是一个目标(尽管它显然是在c源文件中的#included)。当我不尝试使用目录文件的子目录(即错过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似乎没有对目标文件应用隐式规则 “隐式规则告诉如何使用习惯技术,以便在使用它们时不必详细指定它们。例如,C编译有一个隐式规则。文件名确定运行哪些隐式规则。例如,C编译通常采用.c文件并生成.o文件。因此当看到文件名结尾的组合时,make会应用C编译的隐式规则。“以及“在审查隐式规则时,也会在搜索隐式规则时搜索VPATH或vpath中指定的目录(参见使用隐式规则)。”

再次在这里“例如,当文件foo.o没有明确的规则时,make会考虑隐式规则,例如编译foo.c的内置规则(如果该文件存在)。如果这样的文件缺少在当前目录中,搜索相应的目录。如果在任何目录中存在(或在makefile中提到)foo.c,则应用C编译的隐式规则。“

非常感谢任何获取隐式规则以帮助我的makefile的帮助。

编辑2: 感谢Jack Kelly我已经制定了一个明确的规则来编译.c文件,因为我无法在任何地方尝试使用隐式规则。还要感谢al_miro的vpath信息。

这是工作makfile:

.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)

9 个答案:

答案 0 :(得分:60)

由于您正在使用GNUmake,因此请使用模式规则来编译目标文件:

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

答案 1 :(得分:46)

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

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

源文件是通过shell find命令检测到的,因此无需明确指定,只需对您的内容进行编码。

它还会复制“资源”中的所有文件。文件夹,在编译项目时进入bin文件夹,我在大多数时候都很方便。

为了提供应有的信用,自动依赖功能主要基于Scott McPeak的页面,可以找到HERE,并根据我的需要进行一些额外的修改/调整。

示例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

答案 2 :(得分:23)

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

vpath %.c  src
vpath %.h  src

即。不是资本,没有=。就像现在一样,它找不到.h文件并认为它是一个目标。

答案 3 :(得分:5)

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

请查看这两个链接以获得更多解释,以及“聪明”的解决方法。

答案 4 :(得分:2)

以下解决方案在我看来并不好,因为我非常喜欢内置规则。但是,GNU make不支持输出目录vpath之类的东西。并且内置规则无法匹配,因为%中的%.oobj/foo的{​​{1}}匹配,obj/foo.o中的make搜索vpath %.c src/对于像src/obj/foo.c这样的内容,但不是src/foo.c

但这与您可以获得的内置规则非常接近,因此据我所知,这是最好的解决方案。

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

说明:$(COMPILE.c) $(OUTPUT_OPTION) $<实际上是.c.o的实施方式,请参阅http://git.savannah.gnu.org/cgit/make.git/tree/default.c(手册中甚至提到了它)

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

$(OBJDIR):
        mkdir -p $(OBJDIR)

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

.PHONY: clean
clean:
        $(RM) -r $(OBJDIR)

这要求 order-only 功能可用,您可以使用$(filter order-only, $(.FETAURES))进行检查。我已经检查过Kubuntu 14.04 GNU make 3.81和OpenSUSE 13.1 GNU make 3.82。两者都是使用仅订单构建的,现在让我感到困惑的是为什么Kubuntu 14.04带有比OpenSUSE 13.1更旧版本的GNU make。无论如何,现在下载make 4.1:)

答案 5 :(得分: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 (e.g. 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"

通过变量,输出被设置为不同的目录(curdir / objects)。

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

为确保编译器在新对象文件夹中找到对象,该路径将添加到文件名中。

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

这是一个例子,肯定有改进的余地。

有关其他信息,请咨询: Make documetation. See chapter 10.2

或者: Oracle: Programming Utilities Guide

答案 6 :(得分:1)

您可以在compile命令中指定-o $ @选项,以强制compile命令的输出采用目标名称。例如,如果您有:

  • 来源:cpp / class.cpp和cpp / driver.cpp
  • 标题:headers / class.h

...并且您要将对象文件放在以下位置:

  • 对象:obj / class.o obj / driver.o

...然后,您可以将cpp / class.cpp和cpp / driver.cpp分别编译为obj / class.o和obj / driver.o,然后使用以下Makefile进行链接:

CC=c++
FLAGS=-std=gnu++11

INCS=-I./headers
SRC=./cpp
OBJ=./obj
EXE=./exe

${OBJ}/class.o:    ${SRC}/class.cpp
                   ${CC} ${FLAGS} ${INCS} -c $< -o $@

${OBJ}/driver.o:    ${SRC}/driver.cpp ${SRC}/class.cpp
                    ${CC} ${FLAGS} ${INCS} -c $< -o $@

driver: ${OBJ}/driver.o ${OBJ}/class.o
        ${CC} ${FLAGS} ${OBJ}/driver.o ${OBJ}/class.o -o ${EXE}/driver

答案 7 :(得分:1)

对于使用这种目录样式的任何人:

'modules' => {
    'top0' => {
        'instances' => {

以下内容对我来说非常有效。我自己做了,使用 the GNU make manual作为我的主要参考资料; this对我的最后一条规则特别有帮助,最后一条规则对我来说是最重要的。

我的Makefile:

$VAR1 = {
    'modules' => {
        'top0' => {
            'instances' => {
                'sub_top' => {
                    'instances' => {
                        'inst2' => 2,
                        'inst0' => 0,
                        'inst1' => 1
                    }
                }
            }
        }
    }
};

通过使用project > src > pkgA > pkgB ... > bin > pkgA > pkgB ... 作为PROG := sim CC := g++ ODIR := bin SDIR := src MAIN_OBJ := main.o MAIN := main.cpp PKG_DIRS := $(shell ls $(SDIR)) CXXFLAGS = -std=c++11 -Wall $(addprefix -I$(SDIR)/,$(PKG_DIRS)) -I$(BOOST_ROOT) FIND_SRC_FILES = $(wildcard $(SDIR)/$(pkg)/*.cpp) SRC_FILES = $(foreach pkg,$(PKG_DIRS),$(FIND_SRC_FILES)) OBJ_FILES = $(patsubst $(SDIR)/%,$(ODIR)/%,\ $(patsubst %.cpp,%.o,$(filter-out $(SDIR)/main/$(MAIN),$(SRC_FILES)))) vpath %.h $(addprefix $(SDIR)/,$(PKG_DIRS)) vpath %.cpp $(addprefix $(SDIR)/,$(PKG_DIRS)) vpath $(MAIN) $(addprefix $(SDIR)/,main) # main target #$(PROG) : all $(PROG) : $(MAIN) $(OBJ_FILES) $(CC) $(CXXFLAGS) -o $(PROG) $(SDIR)/main/$(MAIN) # debugging all : ; $(info $$PKG_DIRS is [${PKG_DIRS}])@echo Hello world %.o : %.cpp $(CC) $(CXXFLAGS) -c $< -o $@ # This one right here, folks. This is the one. $(OBJ_FILES) : $(ODIR)/%.o : $(SDIR)/%.h $(CC) $(CXXFLAGS) -c $< -o $@ # for whatever reason, clean is not being called... # any ideas why??? .PHONY: clean clean : @echo Build done! Cleaning object files... @rm -r $(ODIR)/*/*.o 的先决条件,这迫使make在源软件包目录中查找源代码,而不是在与目标文件相同的文件夹中查找。

我希望这对某些人有帮助。让我知道您是否对我提供的内容有任何疑问。

顺便说一句:正如您从我的上一条评论中可能会看到的那样,干净并不被称为,我不确定为什么。有什么想法吗?

答案 8 :(得分:0)

这些答案似乎都不够简单-问题的症结在于无需重建:

makefile

OBJDIR=out
VPATH=$(OBJDIR)

# make will look in VPATH to see if the target needs to be rebuilt
test: moo
    touch $(OBJDIR)/$@

示例用法

touch moo
# creates out/test
make test
# doesn't update out/test
make test
# will now update test
touch moo
make test