通过Makefile使用多个源进行分离编译

通过Makefile使用多个源进行分离编译,makefile,Makefile,我编写了一个简单的Makefile来编译几种类型的源代码。而且效果很好。此生成文件将查找所有protobuf源代码和C++源代码。因此,制作该程序将按照以下步骤进行 proto将生成C++源文件,其中包含*。proto g++将使用*.cpp和*.pb.cc编译对象文件(由步骤1生成) g++(实际上是ld)将所有对象*。obj链接到单个可执行文件 此Makefile示例是: # Define the protoc generator PROTOC = protoc INCLUDE = -I$(

我编写了一个简单的Makefile来编译几种类型的源代码。而且效果很好。此生成文件将查找所有
protobuf
源代码和
C++
源代码。因此,制作该程序将按照以下步骤进行

  • proto
    将生成
    C++
    源文件,其中包含
    *。proto
  • g++
    将使用
    *.cpp
    *.pb.cc
    编译对象文件(由步骤1生成)
  • g++
    (实际上是
    ld
    )将所有对象
    *。obj
    链接到单个可执行文件
  • 此Makefile示例是:

    # Define the protoc generator
    PROTOC = protoc
    INCLUDE = -I$(SOMEPLACE) 
    LIB = -lxml2  -lpthread -lz
    
    # The final single outputted executable
    OUTPUT  = svrkit_adapter_v2
    
    # Define the cpp source code/object result
    SOURCES = $(wildcard *.cpp)
    OBJECTS=$(SOURCES:.cpp=.o)
    
    # Define the proto's source/generated result/object result
    PROTO_SOURCES = $(wildcard *.proto)
    PROTO_CPPSRC = $(patsubst %.proto,%.pb.cc,$(PROTO_SOURCES))
    PROTO_OBJECT = $(patsubst %.proto,%.pb.o,$(PROTO_SOURCES))
    
    # First actual making target
    all:$(OUTPUT) 
    
    # Define the rule for generating proto's cpp sources
    $(PROTO_CPPSRC) : $(PROTO_SOURCES)
      $(PROTOC) --cpp_out=. $(filter %.proto, $^) 
    
    # Define the rule for compiling generated proto cpp sources
    $(PROTO_OBJECT) : $(PROTO_CPPSRC)
      $(CXX) $(CPPFLAGS) $(INCLUDE) -c $(filter %.cc, $^) 
    
    # Define the rule for compiling other cpp sources
    $(OBJECTS): $(SOURCES) $(PROTO_OBJECT)
      $(CXX) $(CPPFLAGS) $(INCLUDE) -c $(filter %.cpp, $^) 
    
    # Define the rule for linking the final executable
    $(OUTPUT): $(OBJECTS) $(PROTO_OBJECT)
      $(CXX) $(CFLAGS) -o $@ $^ ${LIB}
    
    clean:
      rm -f *.o *.~ *.bak *.pb.*
      rm -f $(OUTPUT)
    
    但是,make执行的命令行如下(例如):

    有时它工作得很好但是如果源代码太多,则重新编译一些未更改的源代码将花费大量时间。例如,我刚刚修改了
    a.proto
    ,生成文件也将重新编译
    b.proto

    我的问题是:如何使每个源文件分别编译/生成。执行的命令行应为:

    protoc a.proto
    protoc b.proto
    g++ -c a.pb.cc -o a.o
    g++ -c b.pb.cc -o b.o
    g++ -c x.cpp -o x.o
    g++ -c y.cpp -o y.o
    g++ -c z.cpp -o z.o
    g++ -o output a.o b.o x.o y.o z.o
    

    感谢您的高级指导。

    我知道这不是您问题的全部答案,但我认为这可能会有所帮助 你。下面是我使用的makefile的一个示例:

    EXEC=prog1
    EXEC+=prog2
    
    prog1_SRCS=prog1.cpp source1.cpp source2.cpp
    prog2_SRCS=prog2.cpp source3.cpp
    
    #------------------------------------------------------------------
    #no need to change anything below that line (maybe add some OPTION)
    
    CXX = g++ -std=c++11
    
    ERRORS = -Wall -Wextra -pedantic
    OPTION = 
    
    BUILD=build
    
    CXXFLAGS = $(ERRORS) $(OPTION)
    LDFLAGS  = $(ERRORS) $(OPTION)
    
    SRCS=$(wildcard *.cpp)
    
    all: OPTION += -O3 -DNDEBUG
    all:$(EXEC)
    
    debug: OPTION += -ggdb 
    debug:$(EXEC)
    
    .SECONDEXPANSION:
    $(EXEC): $$(patsubst %.cpp, $(BUILD)/%.o, $$($$@_SRCS)) 
        @echo Links $(notdir $^)
        $(CXX) -o $@ $^ $(LDFLAGS) 
    
    $(BUILD)/%.o:%.cpp
        @echo Creates $(notdir $@)
        $(CXX) -MD -c $(CXXFLAGS) $< -o $@
    
    -include $(addprefix $(BUILD)/,$(SRCS:.cpp=.d))
    
    clean:
        rm -f $(BUILD)/* $(EXEC)
    
    EXEC=prog1
    EXEC+=prog2
    prog1_SRCS=prog1.cpp source1.cpp source2.cpp
    prog2_SRCS=prog2.cpp source3.cpp
    #------------------------------------------------------------------
    #无需更改该行下方的任何内容(可能添加一些选项)
    CXX=g++-std=c++11
    错误=-Wall-Wextra-pedantic
    选项=
    建造
    CXXFLAGS=$(错误)$(选项)
    LDFLAGS=$(错误)$(选项)
    SRCS=$(通配符*.cpp)
    全部:选项+=-O3-DNDEBUG
    全部:$(执行)
    调试:选项+=-ggdb
    调试:$(执行)
    .第二次扩展:
    $(EXEC):$$(patsubst%.cpp,$(BUILD)/%.o,$$($@\u SRCS))
    @回显链接$(notdir$^)
    $(CXX)-o$@$^$(LDFLAGS)
    $(构建)/%.o:%.cpp
    @echo创建$(notdir$@)
    $(CXX)-MD-c$(CXXFLAGS)$<-o$@
    -包括$(addprefix$(BUILD)/,$(SRCS:.cpp=.d))
    清洁:
    rm-f$(构建)/*$(执行)
    
    您需要一个名为
    build
    的目录来保存
    .o
    .d
    文件。他们将
    允许您仅重新编译已修改的文件

    我知道这不是您问题的全部答案,但我认为这可能会有所帮助 你。下面是我使用的makefile的一个示例:

    EXEC=prog1
    EXEC+=prog2
    
    prog1_SRCS=prog1.cpp source1.cpp source2.cpp
    prog2_SRCS=prog2.cpp source3.cpp
    
    #------------------------------------------------------------------
    #no need to change anything below that line (maybe add some OPTION)
    
    CXX = g++ -std=c++11
    
    ERRORS = -Wall -Wextra -pedantic
    OPTION = 
    
    BUILD=build
    
    CXXFLAGS = $(ERRORS) $(OPTION)
    LDFLAGS  = $(ERRORS) $(OPTION)
    
    SRCS=$(wildcard *.cpp)
    
    all: OPTION += -O3 -DNDEBUG
    all:$(EXEC)
    
    debug: OPTION += -ggdb 
    debug:$(EXEC)
    
    .SECONDEXPANSION:
    $(EXEC): $$(patsubst %.cpp, $(BUILD)/%.o, $$($$@_SRCS)) 
        @echo Links $(notdir $^)
        $(CXX) -o $@ $^ $(LDFLAGS) 
    
    $(BUILD)/%.o:%.cpp
        @echo Creates $(notdir $@)
        $(CXX) -MD -c $(CXXFLAGS) $< -o $@
    
    -include $(addprefix $(BUILD)/,$(SRCS:.cpp=.d))
    
    clean:
        rm -f $(BUILD)/* $(EXEC)
    
    EXEC=prog1
    EXEC+=prog2
    prog1_SRCS=prog1.cpp source1.cpp source2.cpp
    prog2_SRCS=prog2.cpp source3.cpp
    #------------------------------------------------------------------
    #无需更改该行下方的任何内容(可能添加一些选项)
    CXX=g++-std=c++11
    错误=-Wall-Wextra-pedantic
    选项=
    建造
    CXXFLAGS=$(错误)$(选项)
    LDFLAGS=$(错误)$(选项)
    SRCS=$(通配符*.cpp)
    全部:选项+=-O3-DNDEBUG
    全部:$(执行)
    调试:选项+=-ggdb
    调试:$(执行)
    .第二次扩展:
    $(EXEC):$$(patsubst%.cpp,$(BUILD)/%.o,$$($@\u SRCS))
    @回显链接$(notdir$^)
    $(CXX)-o$@$^$(LDFLAGS)
    $(构建)/%.o:%.cpp
    @echo创建$(notdir$@)
    $(CXX)-MD-c$(CXXFLAGS)$<-o$@
    -包括$(addprefix$(BUILD)/,$(SRCS:.cpp=.d))
    清洁:
    rm-f$(构建)/*$(执行)
    
    您需要一个名为
    build
    的目录来保存
    .o
    .d
    文件。他们将
    允许您仅重新编译已修改的文件

    您犯了一个错误,即规则用于完整列表而不是单个文件,从而剥夺了
    make
    处理单个文件的能力

    您的生成规则:

    # Define the rule for generating proto's cpp sources
    $(PROTO_CPPSRC) : $(PROTO_SOURCES)
      $(PROTOC) --cpp_out=. $(filter %.proto, $^) 
    
    # Define the rule for compiling generated proto cpp sources
    $(PROTO_OBJECT) : $(PROTO_CPPSRC)
      $(CXX) $(CPPFLAGS) $(INCLUDE) -c $(filter %.cc, $^) 
    
    相反,让规则对单个文件起作用

    (为什么
    $(PROTO_OBJECT)
    包含在依赖项中?我觉得这是非常错误的。)

    再次将此选项切换为处理单个文件,而不是整个列表:

    # Define the rule for compiling other cpp sources
    %.o: %.cpp
        $(CXX) $(CPPFLAGS) $(INCLUDE) -c $< -o $@
    
    放下PROTO_CPPSRC,它是不需要的

    现在,
    make
    将查看最后一条规则,找到对象依赖项,并对每个需要重建的对象应用单个文件规则


    与您的问题无关,但在您进行此操作时,请将以下语句添加到Makefile中:

    .PHONY: all clean
    

    这将使“all”和“clean”始终运行,即使存在具有该名称的文件。这通常不是一个问题,但当它发生时,它可能会令人困惑

    您犯了这样一个错误:让规则处理完整列表而不是单个文件,从而剥夺了
    make
    处理单个文件的能力

    您的生成规则:

    # Define the rule for generating proto's cpp sources
    $(PROTO_CPPSRC) : $(PROTO_SOURCES)
      $(PROTOC) --cpp_out=. $(filter %.proto, $^) 
    
    # Define the rule for compiling generated proto cpp sources
    $(PROTO_OBJECT) : $(PROTO_CPPSRC)
      $(CXX) $(CPPFLAGS) $(INCLUDE) -c $(filter %.cc, $^) 
    
    相反,让规则对单个文件起作用

    (为什么
    $(PROTO_OBJECT)
    包含在依赖项中?我觉得这是非常错误的。)

    再次将此选项切换为处理单个文件,而不是整个列表:

    # Define the rule for compiling other cpp sources
    %.o: %.cpp
        $(CXX) $(CPPFLAGS) $(INCLUDE) -c $< -o $@
    
    放下PROTO_CPPSRC,它是不需要的

    现在,
    make
    将查看最后一条规则,找到对象依赖项,并对每个需要重建的对象应用单个文件规则


    与您的问题无关,但在您进行此操作时,请将以下语句添加到Makefile中:

    .PHONY: all clean
    

    这将使“all”和“clean”始终运行,即使存在具有该名称的文件。这通常不是一个问题,但当它发生时,它可能会令人困惑$(PROTO_OBJECT)放入依赖项列表中,确保在编译其他正常cpp源代码之前可以生成
    *.pb.h
    。否则,它当然会
    xx.pb.h
    找不到错误。@AlexanderChen:啊。。。您以前没有提到任何
    *.pb.h
    。这是运行
    $(PROTOC)
    的副作用吗?在这种情况下,您需要确保在
    *.proto
    上一次运行一个源文件而不是完整列表不会破坏任何内容。有些发电机可能是大脑死亡的方式,它会挫败你的努力想出一个分钟