首页 文章

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

提问于
浏览
48

我在尝试使用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:感谢杰克凯利我已经制定了一个明确的规则来编译.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)

6 回答

  • 4

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

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

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

    它允许将源文件, Headers 和内联文件放在子文件夹和子文件夹的子文件夹中等等,并且 automatically generate a dependency file for each object 这意味着修改 Headers 和内联文件将触发重新编译依赖的文件 .

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

    在编译项目时,它还会将所有文件从'resources'文件夹复制到bin文件夹中,这在大多数时候我都很方便 .

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

    Example 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

    VPATH线是错的,它们应该是

    vpath %.c  src
    vpath %.h  src
    

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

  • 40

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

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

  • 58

    以下解决方案不支持 vpath 等输出目录 . 并且内置规则无法匹配,因为 %.o 中的 %obj/foo.oobj/foo 匹配, makevpath %.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)
    

    这要求仅限功能订单可用,您可以使用 $(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 :)

  • 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

相关问题