我一直在这个网站上寻找解决方案,并且也尝试了一段时间的google,但不知为何我无法使用它。
我的源应该在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)/$@
当我使用此变体时,可以看到它尝试构建,但是出现错误,提示“file” .o不适合目标模式。
另一个问题是“$
更新:
同时,我的最新版本如下所示:
$(OBJDIR)/$(OBJ) : $(OBJDIR)/%.o : $(SRCDIR)/%.cpp
$(CC) -S $< -o $(OBJDIR)/`basename $@ .o`.asm
$(CC) -c $< -o $@
现在,这可以编译第一个目标文件(nohupshd.o),但是当make尝试处理第二个目标文件时,它再次失败:目标'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
之类的东西。有许多自动生成变量可以代替使用。例如,$*
扩展到词干,因此您可以编写$(OBJDIR)/$*.asm
。当然,如果您为ASM文件制定单独的模式规则,则可以直接使用$@
或$<
。也可以使用各种make函数。请参阅手册。
第五,您定义了一个包含头文件DEP
的变量,但是从不使用它。因为不使用它,所以如果更改该文件,则不会重建任何文件。如果您知道所有源文件都包含每个 header ,则可以使用$(OBJ) : $(DEP)
进行定义;但这确实意味着(如上述第二点所述)对任何 header 的任何更改都会导致所有对象重新编译。自动生成前提条件会更好。由于您使用的是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 $@