我想让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
你要把协议缓冲区的源文件添加到关心它们的规则的右边(先决条件)。这就是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中运行的。
多亏了@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"