Makefile:没有规则来制作目标

Makefile: no rule to make target

本文关键字:目标 有规则 Makefile      更新时间:2023-10-16

我在这个网站上寻找解决方案,也尝试了谷歌一段时间了,但不知何故我不能让它工作。

我的源文件应该在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)/$@

1变种:

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

当我使用这个模式时,我总是得到一个错误,没有nohupshd规则。

变种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 $@

这现在设法编译第一个objectfile (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文件,而不是直接从源编译到对象,但是如果你真的想要它们,创建一个单独的模式规则来构建它们会更干净,更像"make-like":创建一个模式规则$(OBJDIR)/%.o : $(OBJDIR)/%.asm和一个规则$(OBJDIR)/%.asm : $(SRCDIR)/%.c。如果您希望ASM文件成为构建的产物,您应该将它们声明为all或类似的先决条件,否则它们将作为中间文件被删除。

第四,使用basename之类的东西是不必要的。有很多自动make变量可以用来代替。例如,$*展开为词干,因此可以写成$(OBJDIR)/$*.asm。当然,如果您为ASM文件制定单独的模式规则,您可以直接使用$@$<。有各种make功能也可以使用;

第五,您定义了一个包含头文件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的答案,对于我的错误的解释,请查看我标记为正确答案的帖子:

生成的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)  

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