使用多个目录中的文件生成文件的问题

Issue with Make file with files in multiple directories

我有以下生成文件。当我只有一个目录(SOURCEDIR)中有文件时,这工作正常。现在我添加了 PROTOSOURCES 和 PROTOSRCDIR。

我将在 SRCDIR 中执行 makefile。

VERSION = 0.0.1
CC      = /usr/bin/g++
CFLAGS  = -Wall
LDFLAGS = `pkg-config --cflags --libs protobuf`
SHFLAGS = -shared
SOURCEDIR = .
PROTOSRCDIR = ~/depot/Projects/src1/obj/cpp
SOURCES = $(wildcard $(SOURCEDIR)/*.cpp)
PROTOSOURCES = $(wildcard $(PROTOSRCDIR)/*.cc)
SRCOBJECTS = $(patsubst $(SOURCEDIR)/%.cpp, $(SOURCEDIR)/%.o, $(SOURCES))
PROTOOBJECTS = $(patsubst $(PROTOSRCDIR)/%.cc, $(PROTOSRCDIR)/%.o, $(PROTOSOURCES))

LIBRARY = libprotointf.so

.PHONY: all clean

all: $(LIBRARY)

$(LIBRARY): 
    $(CC) $(SHFLAGS) $(CFLAGS) $(LDFLAGS) $(SRCOBJECTS) $(PROTOOBJECTS) -o    $(LIBRARY)

$(SOURCEDIR)/%.o: $(SOURCEDIR)/%.cpp
    $(CC) $(CFLAGS) $(LDFLAGS) -c -fPIC $< -o $@

$(PROTOSRCDIR)/%.o: $(PROTOSRCDIR)/%.cc
    $(CC) $(CFLAGS) $(LDFLAGS) -c -fPIC $< -o $@

clean:
    rm -f libprotointf.so

我遇到以下问题。

问题 1:SOURCEOOBJECTS 符合预期,列出了 SOURCEDIR 中的所有目标文件。然而,PROTOOBJECTS 只列出 *.cc 文件而不是 *.o 文件。我希望它在与 patsubst 一起使用后在 PROTOOBJECTS 中包含所有目标文件列表。顺便说一句,SOURCEDIR 有 *.cpp 文件,而 PROTOSRCDIR 有 .pb.cc 文件。这些是由 Google Protocol Buffer 编译器生成的。

问题 2:
这将是我的 make 文件的最终目标。
1) 编译 SRCDIR 中的文件并生成 .o 文件
2) 编译 PROTOSRCDIR 中的文件并生成 .o 文件
3) 生成一个共享库 libprotointf.so 文件,其中包含上述两个步骤中的所有目标文件。

当我只有 SRCDIR 中的文件时,我的库链接和编译命令如下所示,工作正常。

$(LIBRARY): 
    $(CC) $(SHFLAGS) $(CFLAGS) $(LDFLAGS) $(SRCOBJECTS) -o $(LIBRARY)
$(SOURCEDIR)/%.o: $(SOURCEDIR)/%.cpp
    $(CC) $(CFLAGS) $(LDFLAGS) -c -fPIC $< -o $@

为了制作共享库,我需要对 Makefile 做哪些修改?

我建议使用 make 的 VPATH 特性。这告诉 make 也在其他目录中寻找 "sources" 。所有目标文件都将与共享库一起位于构建目录中。

# Note: GNU make syntax
VERSION := 0.0.1
CXX     := /usr/bin/g++
CXXFLAGS:= -Wall -fPIC
LDFLAGS := $(shell pkg-config --cflags --libs protobuf)
SHFLAGS := -shared
SOURCEDIR := .
PROTOSRCDIR := ~/depot/Projects/src1/obj/cpp

VPATH := $(SOURCEDIR):$(PROTOSRCDIR)

SOURCES := $(wildcard $(SOURCEDIR)/*.cpp) # full paths
SOURCEFILES := $(notdir $(SOURCES)) # just (source) filenames
OBJECTS := $(SOURCEFILES:.cpp=.o) # all in the current dir

PROTOSOURCES = $(wildcard $(PROTOSRCDIR)/*.cc) # full paths
PROTOSOURCEFILES := $(notdir $(PROTOSOURCES)) # just (source) filenames
PROTOOBJECTS := $(PROTOSOURCEFILES:.cpp=.o) # all in the current dir

LIBRARY = libprotointf.so

.PHONY: all clean

all: $(LIBRARY)

$(LIBRARY): $(OBJECTS) $(PROTOOBJECTS)
    $(CC) $(SHFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^

注意:没有 %.o: %.cc%.o: %.cpp 规则。 GNU make 内置了这些。您只需填写 CPPFLAGSCXXFLAGS

的值

请注意,按照惯例,CC 是包含 C 编译器的变量。 C++编译器的变量是CXX。同样,CFLAGS 用于 C 编译器; CXXFLAGS 是包含 C++ 编译器标志的变量。

这是如何工作的:

假设您在 SOURCEDIR 中有 s1.cpp 和 s2.cpp,在 PROTOSRCDIR 中有 p1.pb.cc、p2.pb.cc,libprotoinf.so 取决于 s1.o,s2.o, p1.pb.o, p2.pb.o

Make 可以使用其内置的 %.o: %.cpp 规则轻松地从 s1.cpp 构建 s1.o 并从 s2.cpp 构建 s2.o。当它尝试构建 p1.pb.o 时,它不会在当前目录中找到 p1.pb.cc,但 VPATH 变量告诉它还要在 PROTOSRCDIR 中查找。然后 make 将从 $(PROTOSRCDIR)/p1.pb.cc 构建 p1.pb.o,就好像 cc 文件存在于当前目录中一样。

使用这种技术,您甚至可以在不同于 SOURCEDIR 的单独构建目录中进行构建。