Makefile:没有规则来制作目标

我正在寻找这个网站上的解决方案,现在也尝试谷歌一段时间了,但不知怎的,我无法让它工作。

我的源应该在src目录中,目标文件将在obj目录中。 现在我尝试创建一个简单的makefie但我得到一个没有规则的错误,或者我无法使它工作使用目录。

CC = /usr/bin/gcc CXXFLAGS = -O2 -g -Wall -fmessage-length=0 SRC:= nohupshd.cpp \ task.cpp OBJ:= nohupshd.o \ task.o OBJDIR:= obj SRCDIR:= src DEP:= src/task.h LIBS:= TARGET:= nohupshd all: $(TARGET) $(TARGET): $(OBJ) $(CC) -o $(TARGET) $(OBJ) $(LIBS) clean: rm -f $(OBJ) $(TARGET) 

变式1:

 $(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(CC) -S $(SRCDIR)/$< -o $(OBJDIR)/$@ $(CC) -c $(SRCDIR)/$< -o $(OBJDIR)/$@ 

变式1a:

 %.o: %.cpp $(CC) -S $(SRCDIR)/$< -o $(OBJDIR)/$@ $(CC) -c $(SRCDIR)/$< -o $(OBJDIR)/$@ 

当我使用这个模式时,我总是会遇到一个错误,即nohupshd.o没有构建规则。

变式2:

 $(OBJ) : $(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(CC) -S $(SRCDIR)/$< -o $(OBJDIR)/$@ $(CC) -c $(SRCDIR)/$< -o $(OBJDIR)/$@ 

当我使用这个变体时,我可以看到它试图构建,但是我得到错误,说“文件”.o不适合目标模式。

另一个问题是“$ <”没有给我源名称。 根据几个网站它应该,但我可以在输出中看到什么都没有,所以我该如何解决这个问题呢?

更新:

与此同时,我的最新版本如下所示:

 $(OBJDIR)/$(OBJ) : $(OBJDIR)/%.o : $(SRCDIR)/%.cpp $(CC) -S $< -o $(OBJDIR)/`basename $@ .o`.asm $(CC) -c $< -o $@ 

这现在设法编译第一个目标文件(nohupshd.o),但是当make尝试执行第二个文件时,它再次失败说:target’task.o’与模式不匹配。

如果不正确的话,你实际上有几个。

首先你写的我的错误是,我假设模式%.o匹配以.o结尾的任何模式,而不是 ; 这不是真的。 该模式匹配任何以.o结尾的字符串。 但是,在目标端匹配的模式字符%在先决条件侧被替换为相同的字符串。 因此,如果你有一个目标obj/task.o并且它匹配模式%.o那么词干 (手动调用它)将是obj/task ,当前提条件是%.c ,这意味着make将查找一个先决条件obj/task.c 由于没有一个,并且make不知道如何构建一个,该规则被丢弃为不应用。 编写模式规则时,必须编写它们,以便名称的相同部分与模式字符( % )匹配。 必须明确指定所有不相同的部分,包括目录。

其次,规则$(OBJ) : $(SRC)真的不对。 该行表示每个目标文件都依赖于所有源文件,因此每当任何单个源文件发生更改时, all对象文件都将被重新编译。 这真的不是你想要的(如果那是你想要的,你不需要make:你可以写一个简单的shell脚本)。 我不知道你的意思, 因为规则是空的,它调用模式规则 ; 你不需要这个来调用模式规则。 目标取决于$(OBJ) ,每个目标文件都依赖于其源文件(由于模式)。 你根本不需要这条线。

第三,我不知道你为什么要尝试构建.asm文件,而不是直接从源代码编译到对象,但是如果你真的想要它们,那么创建一个单独的模式规则会更清晰,更“仿制”构建它们:创建模式规则$(OBJDIR)/%.o : $(OBJDIR)/%.asm和规则$(OBJDIR)/%.asm : $(SRCDIR)/%.c 。 如果您希望ASM文件是构建的产品,您应该将它们声明为all或类似的先决条件,否则它们将被删除为中间文件。

第四,使用basename东西是不必要的。 可以使用许多自动make变量。 例如, $*扩展为词干,因此您可以编写$(OBJDIR)/$*.asm 。 当然,如果您为ASM文件制定单独的模式规则,则可以直接使用$@$< 。 还有各种makefunction,也可以使用; 见手册。

第五,你定义一个包含头文件DEP的变量,但从不使用它。 因为它没有被使用,如果你改变那个文件什么都不会重建。 如果您知道所有源文件都包含每个头文件,您可以使用$(OBJ) : $(DEP)来定义它; 但它确实意味着(如上面的第二点)任何标题的任何更改都会导致所有对象重新编译。 你最好自动生成先决条件; 既然你正在使用GCC,这很简单。

第六,你正在使用C ++文件(xxx.cpp),但你正在使用C编译器。 这不起作用(链接行将失败:虽然编译器可以看到你正在编译一个C ++文件并做正确的事情,即使你调用gcc,当你将一堆对象链接在一起时,它也不知道这些是C对象或C ++对象(或FORTRAN或其他)因此您必须使用C ++前端进行链接,否则它将无法引入正确的C ++库)。 您应该使用make变量CXX来构建C ++代码,而不是CC ,并将其设置为g++而不是gcc

第七,你不需要.SUFFIXES: .c .o 。来使用模式规则。 它们仅用于后缀规则,这是您没有的。 你可以保持简单.SUFFIXES:虽然禁用内置模式匹配,这是一个轻微的性能提升。

最后,您会注意到您实际上并不需要$(SRC)变量,因为make可以从模式规则中推断出它。 但是,如果你想让你的makefile不那么繁重,你可以从SRC变量构造OBJ变量的内容,比如SRC = nohupshd.cpp task.cpp然后OBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(SRC))

所以,全押,这就是我建议你编写你的makefile的方法(虽然这里我不包括自动生成的依赖项):

 .SUFFIXES: CXX := g++ CXXFLAGS := -O2 -g -Wall -fmessage-length=0 OBJDIR := obj SRCDIR := src TARGET := nohupshd SRC := nohupshd.cpp task.cpp DEP := src/task.h LIBS := # ---- OBJ := $(patsubst %.cpp,$(OBJDIR)/%.o,$(SRC)) ASM := $(patsubst %.cpp,$(OBJDIR)/%.asm,$(SRC)) .PHONY: all clean all: $(TARGET) $(ASM) $(TARGET): $(OBJ) $(CXX) -o $@ $^ $(LIBS) clean: rm -f $(OBJDIR)/* $(TARGET) $(OBJDIR)/%.o : $(SRCDIR)/%.asm $(CXX) $(CXXFLAGS) -c -x assembler-with-cpp $< -o $@ $(OBJDIR)/%.asm : $(SRCDIR)/%.cpp $(CXX) $(CPPFLAGS) -S $< -o $@ 

不要在编译器行中重复目录名称。 $<$@已经有了目录名。

 $(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(CC) -S $< -o $@ $(CC) -c $< -o $@ 

所以最后我找到了关于如何编写这个makefile的答案,为了揭露我的错误,看看我标记为正确答案的post:

生成的makefile看起来像这样,为了完整性我在这里发布它包括头文件的依赖项(如果你不需要它们,删除ASM部分):

 .SUFFIXES: .SUFFIXES: .o .cpp .SUFFIXES: .o .d CC := g++ LNK:= ar CXXFLAGS = -O2 -g -Wall -fmessage-length=0 OBJDIR:= obj SRCDIR:= src HDIR:= include INCLUDE_PATHS:= -Iinclude -Iinclude/interfaces -Iinclude/support CPP_FILES := propertyfile/propertyfile.cpp \ propertyfile/propertyitem.cpp \ propertyfile/propertyfactory.cpp OBJ := $(patsubst %.cpp,$(OBJDIR)/%.o, $(CPP_FILES)) SRC := $(patsubst %.cpp,$(SRCDIR)/%.o, $(CPP_FILES)) ASM := $(patsubst %.cpp, $(OBJDIR)/$*.asm, $(CPP_FILES)) LIBS:= TARGET:= libsupport.a all: $(TARGET) $(TARGET): $(OBJ) @echo "Linking..." @$(LNK) rvs $(TARGET) $(OBJ) @cp $(TARGET) ../lib @cp -r include .. clean: rm -f $(OBJ) $(ASM) $(TARGET) -include $(patsubst %.cpp,$(OBJDIR)/%.d, $(CPP_FILES)) $(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(OBJDIR)/%.d @mkdir -p `dirname $@` $(CC) $(CXXFLAGS) -S $< -o $(OBJDIR)/$*.asm $(INCLUDE_PATHS) $(CC) $(CXXFLAGS) -c $< -o $@ $(INCLUDE_PATHS) $(OBJDIR)/%.d: $(SRCDIR)/%.cpp $(CC) $(CXXFLAGS) -MM -MT $@ -MF $(OBJDIR)/$*.d -c $< $(INCLUDE_PATHS) 

我希望这有助于其他用户。 我发现的所有示例都非常简单,并且单独列出了多个文件而不是规则的一部分,但没有真正解释它是如何工作的,或者是如此复杂以至于我无法找到它如何帮助我。