正如gcc 应该告诉你的,你不能将-c 用于多个输入文件,所以
gcc -c $(HEADERFILES) $(SOURCEFILES) -o sumprime.o
不起作用。
幸运的是,这也不是必需的;事实上,.o 文件不需要特殊规则,因为built-in rules 工作得很好。尤其如此,因为输出二进制文件的名称与其中一个目标文件相对应(sumprime.o;请参阅链接后面的“链接单个目标文件”)。
我会使用类似的东西
#!/usr/bin/make -f
CC = gcc
CPPFLAGS = -MD
CFLAGS = -O2 -g
LDFLAGS =
LDLIBS =
TARGET = sumprime
HEADERFILES = semaphore.h sharedmemory.h
SOURCEFILES = sumprime.c semaphore.c sharedmemory.c
DISTFOLDER = lab5
DISTFILES = $(HEADERFILES) $(SOURCEFILES) Makefile
HANDIN = $(DISTFOLDER).tar.bz2
OBJFILES = $(SOURCEFILES:.c=.o)
DEPFILES = $(OBJFILES:.o=.d)
all: $(TARGET)
$(TARGET): $(OBJFILES)
clean:
rm -f $(TARGET) $(OBJFILES)
distclean: clean
rm -f $(DEPFILES) $(HANDIN)
pack: dist
dist: $(HANDIN)
$(HANDIN): $(DISTFILES)
@echo [DIST] Preparing for packaging...
@rm -f $@
@tar cjf $@ --transform 's,^,$(DISTFOLDER)/,' $+
@echo [DIST] Done!
.PHONY: all clean distclean dist pack
-include $(DEPFILES)
显然,这需要一些解释。
说明
隐式规则
我在上面提到了这些:make 预定义了许多规则,这些规则通常只是 Do The Right Thing™;我们可以让他们完成我们的大部分工作。事实上,您可以用来构建程序的最短 Makefile 是
sumprime: sumprime.o semaphore.o sharedmemory.o
这使用隐式规则来构建 .o 文件和隐式配方来构建 sumprime。请注意,有些变量会影响隐式规则的行为;在上面的链接后面是这些规则的列表,其中包括它们的配方,以及它们使用的变量的名称。由于我们正在编译 C 代码,因此我们感兴趣的是:
CPPFLAGS = -MD # C preprocessor flags, such as -Ipath -DMACRO=definition
CFLAGS = -O2 -g # C compiler flags
LDFLAGS = # linker flags, such as -Lpath
LDLIBS = # linked libraries, such as -lpthread (Alternatively:
# LOADLIBES, but this is less usual)
模式替换
线条
OBJFILES = $(SOURCEFILES:.c=.o)
DEPFILES = $(OBJFILES:.o=.d)
使用模式替换从.c 文件列表生成.o 文件列表,并从.o 生成.d。我们将使用.d 文件进行依赖跟踪。
依赖跟踪
这可能是最复杂的部分,但也没有那么糟糕。
最小 Makefile 的一个实际问题是它不知道#includes。如果sumprime.c 包含semaphore.h 并且semaphore.h 被更改,我们真的希望sumprime.c 被重建。幸运的是,gcc 有一种机制来促进这一点,我们可以调用
CPPFLAGS = -MD
当给定此选项时,预处理器生成一个.d 文件,对应于给定的输入.c(即,如果编译sumprime.c,则生成sumprime.d),其中包含一个make-compatible列表源文件的依赖关系。例如,我希望sumprime.d 看起来像
sumprime.c: semaphore.h sharedmemory.h
然后,用
-include $(DEPFILES)
make 被指示将这些文件包含在其代码中(如果它们存在)。这意味着 make 总是知道源文件的依赖关系,就像它们在上次构建期间一样(!)。它落后一个不是问题,因为依赖项的更改需要更改目标上次依赖的文件之一,并且第一次没有提取任何依赖项不是问题,因为第一次一切无论如何都必须建造。
因此,我们可以轻松地进行依赖跟踪。
使用 GNU tar 打包
pack: dist
dist: $(HANDIN)
$(HANDIN): $(DISTFILES)
@echo [DIST] Preparing for distaging...
@rm -f $@
@tar cjf $@ --transform 's,^,$(DISTFOLDER)/,' $+
@echo [DIST] Done!
rule 需要 GNU tar,但如果它可用,它的 --transform 选项会形成更好的 dist 规则,如您所见。我冒昧地把它改成了那样。当然,如果您愿意,您仍然可以使用旧方式。
旁注:更常见的做法是称您为pack 规则dist。这没有技术原因,这只是一个约定;人们期待make dist。使用此代码,两个名称都可以使用。