让Make自动检测协议缓冲区的变化。

问题描述 投票:1回答:1

我想让Make在我更新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 ../

它成功地编译了protos;但是,它每次都这样做,即使文件没有变化。如何防止这种情况发生,只有在protos发生变化时才会编译protos?

编辑:添加了项目结构

    ├── LICENSE
    ├── makefile
    ├── protos
    │   ├── client.proto
    │   └── person.proto
    ├── README.md
    └── src
        ├── db_handler
        │   ├── db_handler.cc
        │   └── db_handler.h
        ├── main.cc
        └── protos
            ├── client.grpc.pb.cc
            ├── client.grpc.pb.h
            ├── client.pb.cc
            ├── client.pb.h
            ├── person.grpc.pb.cc
            ├── person.grpc.pb.h
            ├── person.pb.cc
            └── person.pb.h
c++ c makefile protocol-buffers
1个回答
2
投票

你要把协议缓冲区的源文件添加到关心它们的规则的右边(先决条件)。这就是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_SOURCES) 如果你使用并行构建的话,就不太妙了;因为,Make会尝试为每个输出文件运行一次命令。所以,Make会同时运行N个命令实例,这意味着它们可能会互相残杀。

为了知道完全正确的解决方案,你需要提供更多的信息(对于不熟悉protoc的人来说)。是否要求你对所有输入都调用一次protoc?还是在每个输入的.proto文件上分别运行protoc以获得其输出是有效的?然后你可以写一个模式规则,每次只生成一个文件。


注1:对于你的另一个尝试,如果你使用的是 .PHONY 来标记规则,那么Make每次都会重建规则,不管它是否需要。

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


1
投票

多亏了@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"
© www.soinside.com 2019 - 2024. All rights reserved.