Makefile 让Make自动检测协议缓冲区中的更改

Makefile 让Make自动检测协议缓冲区中的更改,makefile,Makefile,我想在更新protos时自动编译protos,以下是我到目前为止得到的: TARGET=main BIN_DIR=bin SRC_DIR=src OBJ_DIR=obj PROTO_DIR=protos/ PROTO_COMPILE_DIR=src/$(PROTO_DIR) CC = g++ CFLAGS = -Wall -std=c++17 -ggdb -pipe -I. LINKER = g++ LFLAGS = $(CFLAGS) -lprotobuf SO

我想在更新protos时自动编译protos,以下是我到目前为止得到的:

TARGET=main

BIN_DIR=bin
SRC_DIR=src
OBJ_DIR=obj

PROTO_DIR=protos/
PROTO_COMPILE_DIR=src/$(PROTO_DIR)

CC       = g++
CFLAGS   = -Wall -std=c++17 -ggdb -pipe -I.
LINKER   = g++
LFLAGS   = $(CFLAGS) -lprotobuf

SOURCES = $(wildcard src/*.cc) \
      $(wildcard src/protos/*.cc) \
      $(wildcard src/db_handler/*.cc)

OBJECTS  := $(SOURCES:$(SRC_DIR)/%.cc=$(OBJ_DIR)/%.o)

$(BIN_DIR)/$(TARGET): proto $(OBJECTS) 
    @mkdir -p $(BIN_DIR)/
    $(LINKER) $(OBJECTS) $(LFLAGS) -o $@

$(OBJECTS): $(OBJ_DIR)/%.o : $(SRC_DIR)/%.cc
    @mkdir -p obj/ obj/protos obj/db_handler
    $(CC) $(CFLAGS) -c $< -o $@

.PHONY: proto
proto:
    @printf "Compiling protos...\n"
    @cd $(PROTO_DIR) && protoc * --grpc_out=../$(PROTO_COMPILE_DIR)\
        --cpp_out=../$(PROTO_COMPILE_DIR)\
        --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin"\
        && cd ../

多亏了@FiddlingBits,我终于找到了正确的方法

TARGET=main

BIN_DIR=bin
SRC_DIR=src
OBJ_DIR=obj

PROTO_DIR=protos/
PROTO_COMPILE_DIR=src/$(PROTO_DIR)

rm       = rm -f

CC       = g++
CFLAGS   = -Wall -std=c++17 -ggdb -pipe -I.
LINKER   = g++
LFLAGS   = $(CFLAGS) -lprotobuf

SOURCES = $(wildcard src/*.cc) \
      $(wildcard src/protos/*.cc) \
      $(wildcard src/db_handler/*.cc)

OBJECTS  := $(SOURCES:$(SRC_DIR)/%.cc=$(OBJ_DIR)/%.o)
PROTOS  := $($(PROTO_DIR)/%.proto=$(PROTO_COMPILE_DIR)/%.cc)

$(BIN_DIR)/$(TARGET): $(PROTOS) $(OBJECTS) 
    echo $(PROTOS)
    @mkdir -p $(BIN_DIR)/
    $(LINKER) $(OBJECTS) $(LFLAGS) -o $@

$(OBJECTS): $(OBJ_DIR)/%.o : $(SRC_DIR)/%.cc
    @mkdir -p obj/ obj/protos obj/db_handler
    $(CC) $(CFLAGS) -c $< -o $@

$(PROTOS):
    @printf "Compiling protos...\n"
    @cd $(PROTO_DIR) && protoc * --grpc_out=../$(PROTO_COMPILE_DIR)\
        --cpp_out=../$(PROTO_COMPILE_DIR)\
        --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin"\
        && cd ../

.PHONY: clean
clean:
    @$(rm) -r $(OBJ_DIR)/*
    @$(rm) -r $(BIN_DIR)/*
    @printf "Cleanup complete!\n"

多亏了@FiddlingBits,我终于找到了正确的方法

TARGET=main

BIN_DIR=bin
SRC_DIR=src
OBJ_DIR=obj

PROTO_DIR=protos/
PROTO_COMPILE_DIR=src/$(PROTO_DIR)

rm       = rm -f

CC       = g++
CFLAGS   = -Wall -std=c++17 -ggdb -pipe -I.
LINKER   = g++
LFLAGS   = $(CFLAGS) -lprotobuf

SOURCES = $(wildcard src/*.cc) \
      $(wildcard src/protos/*.cc) \
      $(wildcard src/db_handler/*.cc)

OBJECTS  := $(SOURCES:$(SRC_DIR)/%.cc=$(OBJ_DIR)/%.o)
PROTOS  := $($(PROTO_DIR)/%.proto=$(PROTO_COMPILE_DIR)/%.cc)

$(BIN_DIR)/$(TARGET): $(PROTOS) $(OBJECTS) 
    echo $(PROTOS)
    @mkdir -p $(BIN_DIR)/
    $(LINKER) $(OBJECTS) $(LFLAGS) -o $@

$(OBJECTS): $(OBJ_DIR)/%.o : $(SRC_DIR)/%.cc
    @mkdir -p obj/ obj/protos obj/db_handler
    $(CC) $(CFLAGS) -c $< -o $@

$(PROTOS):
    @printf "Compiling protos...\n"
    @cd $(PROTO_DIR) && protoc * --grpc_out=../$(PROTO_COMPILE_DIR)\
        --cpp_out=../$(PROTO_COMPILE_DIR)\
        --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin"\
        && cd ../

.PHONY: clean
clean:
    @$(rm) -r $(OBJ_DIR)/*
    @$(rm) -r $(BIN_DIR)/*
    @printf "Cleanup complete!\n"

您希望将协议缓冲区的源文件添加到与之相关的规则的先决条件的右侧。这就是Make如何理解和跟踪他们的时间戳

通过将文件添加到先决条件中,Make将了解这是关心这些源文件的规则

PROTO_SOURCES  := $(wildcard $(PROTO_DIR)/*.proto)
PROTOS         := $(patsubst $(PROTO_DIR)/%.proto,$(PROTO_COMPILE_DIR)/%.cc,$(PROTO_SOURCES))

$(PROTOS): $(PROTO_SOURCES)
    @printf "Compiling protos...\n"
    @cd $(PROTO_DIR) && protoc * --grpc_out=../$(PROTO_COMPILE_DIR)\
        --cpp_out=../$(PROTO_COMPILE_DIR)\
        --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin"
然而,如果您使用并行构建,这个$PROTOS:$PROTO_源代码是不好的;因为,Make将尝试为每个输出文件运行一次命令。因此,make将同时运行N个命令实例,这意味着它们可能会相互碰撞

为了了解完全正确的解决方案,您需要为那些不熟悉protoc的人提供更多信息。是否要求对所有输入调用protoc一次?或者对每个input.proto文件单独运行protoc以获得其输出是否有效?然后您可以编写一个模式规则,一次生成一个文件

注1:至于您的其他尝试,如果您使用.PHONY标记规则,则Make将在每次需要或不需要时重建规则


注2:您不需要cd。。在第二条指令的末尾,因为它是在子shell中运行的。

您希望将协议缓冲区的源文件添加到与之相关的规则的先决条件的右侧。这就是Make如何理解和跟踪他们的时间戳

通过将文件添加到先决条件中,Make将了解这是关心这些源文件的规则

PROTO_SOURCES  := $(wildcard $(PROTO_DIR)/*.proto)
PROTOS         := $(patsubst $(PROTO_DIR)/%.proto,$(PROTO_COMPILE_DIR)/%.cc,$(PROTO_SOURCES))

$(PROTOS): $(PROTO_SOURCES)
    @printf "Compiling protos...\n"
    @cd $(PROTO_DIR) && protoc * --grpc_out=../$(PROTO_COMPILE_DIR)\
        --cpp_out=../$(PROTO_COMPILE_DIR)\
        --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin"
然而,如果您使用并行构建,这个$PROTOS:$PROTO_源代码是不好的;因为,Make将尝试为每个输出文件运行一次命令。因此,make将同时运行N个命令实例,这意味着它们可能会相互碰撞

为了了解完全正确的解决方案,您需要为那些不熟悉protoc的人提供更多信息。是否要求对所有输入调用protoc一次?或者对每个input.proto文件单独运行protoc以获得其输出是否有效?然后您可以编写一个模式规则,一次生成一个文件

注1:至于您的其他尝试,如果您使用.PHONY标记规则,则Make将在每次需要或不需要时重建规则


注2:您不需要cd。。在第二条指令的末尾,因为它是在子shell中运行的。

因为您标记了它。是假的,对吗?这不总是让它过时吗?实际上有一个名为proto的文件吗?如果它不存在,Make可能会始终运行该规则。因此,您没有专门命名为proto的文件。例如,您可能需要名为client.grpc.pb.cc的规则。制作肯定很棘手。你的想法奏效了!谢谢你,先生,因为你做了标记。是假的,对吗?这不总是让它过时吗?实际上有一个名为proto的文件吗?如果它不存在,Make可能会始终运行该规则。因此,您没有专门命名为proto的文件。例如,您可能需要名为client.grpc.pb.cc的规则。制作肯定很棘手。你的想法奏效了!谢谢,先生,这很好,但是现在如果我删除src/protos/*规则根本不运行,知道为什么吗?对不起,我在复制变量定义之前没有检查它们。我现在已经验证过了。您想用单个文件模式描述规则及其前提条件$OBJ_DIR/%.o:$SRC_DIR/%.cc$PROTOS如果您需要跟踪更复杂的依赖项,您可以手动描述它们,也可以查看它们。此$PROTOS:$PROTO_源不正确。实际上,您永远不希望在一个显式规则中有多个目标。如果您总是以串行方式运行,这可能是偶然的,但是如果您使用-j运行,它将很难失败。为了知道正确的解决方案,您需要为那些不熟悉protoc的人提供更多信息。是否要求对所有输入调用protoc一次?或者对每个input.proto文件单独运行protoc以获取其输出是否有效?因为make将尝试对每个输出文件运行一次命令。如果您是串行运行的,那么它将运行该命令一次,然后所有后续命令将不会运行,因为第一个命令将使它们最新。但如果并行运行,make将同时运行N个命令实例,因为在进行检查时,目标已过期,这意味着它们可能会相互覆盖。这很好,但现在如果我删除src/protos/*则规则根本不会运行,知道原因吗?抱歉,在复制变量定义之前,我没有检查它们。我已经验证过了。你想用一个文件来描述规则吗
模式及其先决条件$OBJ_DIR/%.o:$SRC_DIR/%.cc$PROTOS如果您需要跟踪更复杂的依赖项,您可以手动描述它们,也可以查看它们。此$PROTOS:$PROTO_源不正确。实际上,您永远不希望在一个显式规则中有多个目标。如果您总是以串行方式运行,这可能是偶然的,但是如果您使用-j运行,它将很难失败。为了知道正确的解决方案,您需要为那些不熟悉protoc的人提供更多信息。是否要求对所有输入调用protoc一次?或者对每个input.proto文件单独运行protoc以获取其输出是否有效?因为make将尝试对每个输出文件运行一次命令。如果您是串行运行的,那么它将运行该命令一次,然后所有后续命令将不会运行,因为第一个命令将使它们最新。但是,如果并行运行,make将同时运行N个命令实例,因为进行检查时,目标已过期,这意味着它们可能会相互覆盖。