使用Makefile编译C++项目报错:No rule to make target
问题描述
我的C++项目结构如下:
/root --/Client ----/build ----/include ----/src --/Common ----/include ----/src --/Server ----/build ----/include ----/src --Makefile
需求:通过根目录下的单个Makefile编译客户端和服务端,.o文件生成在各自的/build目录中,可执行文件放在/root目录下,同时需要编译目标模块和/Common模块的文件。
我编写的Makefile如下:
CC := g++ CFLAGS := -Wall -Wextra -I./Common/include -I./Server/include -I./Client/include LDFLAGS := -lws2_32 CLIENT_SRC_DIR := ./Client/src CLIENT_BUILD_DIR := ./Client/build CLIENT_BIN_DIR := ./Client/bin CLIENT_EXE := ./Client/bin/client.exe SERVER_SRC_DIR := ./Server/src SERVER_BUILD_DIR := ./Server/build SERVER_BIN_DIR := ./Server/bin SERVER_EXE := ./Server/bin/server.exe COMMON_SRC_DIR := ./Common/src COMMON_BUILD_DIR := ./Common/build CLIENT_SRCS := $(wildcard $(CLIENT_SRC_DIR)/*.cpp) CLIENT_OBJS := $(patsubst $(CLIENT_SRC_DIR)/%.cpp,$(CLIENT_BUILD_DIR)/%.o,$(CLIENT_SRCS)) SERVER_SRCS := $(wildcard $(SERVER_SRC_DIR)/*.cpp) SERVER_OBJS := $(patsubst $(SERVER_SRC_DIR)/%.cpp,$(SERVER_BUILD_DIR)/%.o,$(SERVER_SRCS)) COMMON_SRCS := $(wildcard $(COMMON_SRC_DIR)/*.cpp) .PHONY: all clean fclean dirs all: client server client: $(CLIENT_EXE) server: $(SERVER_EXE) $(CLIENT_EXE): $(CLIENT_OBJS) $(CC) $(CFLAGS) -o $@ $(CLIENT_OBJS) $(LDFLAGS); $(SERVER_EXE): $(SERVER_OBJS) | $(SERVER_BIN_DIR) $(CC) $(CFLAGS) -o $@ $(SERVER_OBJS) $(LDFLAGS); $(CLIENT_BUILD_DIR)/%.o: $(CLIENT_SRC_DIR)/%.cpp $(COMMON_SRC_DIR)/%.cpp $(CC) $(CFLAGS) $^ -o $@; $(SERVER_BUILD_DIR)/%.o: $(SERVER_SRC_DIR)/%.cpp $(COMMON_SRC_DIR)/%.cpp $(CC) $(CFLAGS) $^ -o $@; dirs: mkdir "$(CLIENT_BUILD_DIR)"; mkdir "$(CLIENT_BIN_DIR)"; mkdir "$(SERVER_BUILD_DIR)"; mkdir "$(SERVER_BIN_DIR)"; clean: rm -r $(CLIENT_BUILD_DIR) $(SERVER_BUILD_DIR); fclean: clean rm -r $(CLIENT_BIN_DIR) $(SERVER_BIN_DIR); re: fclean all
运行时出现报错:
make: *** No rule to make target 'Client/build/main.o', needed by 'Client/bin/client.exe'. Stop.
我知道该报错通常是文件缺失导致,但我使用了通配符%,为何还是无法正常工作?恳请帮忙解决该问题。
问题分析与解决
错误原因
你的规则$(CLIENT_BUILD_DIR)/%.o: $(CLIENT_SRC_DIR)/%.cpp $(COMMON_SRC_DIR)/%.cpp存在逻辑错误:这个规则要求每个客户端的.cpp文件都必须对应一个同名的Common模块.cpp文件才能生成.o文件。比如生成Client/build/main.o时,Make会寻找Client/src/main.cpp和Common/src/main.cpp,如果Common模块没有同名文件,Make就找不到匹配的规则,直接报错。
另外,你还没处理Common模块的编译——Common的源码需要先编译成.o,再链接到客户端和服务端的可执行文件中。
修正后的Makefile
CC := g++ CFLAGS := -Wall -Wextra -I./Common/include -I./Server/include -I./Client/include LDFLAGS := -lws2_32 # 客户端相关路径 CLIENT_SRC_DIR := ./Client/src CLIENT_BUILD_DIR := ./Client/build CLIENT_EXE := ./client.exe # 可执行文件放到根目录 # 服务端相关路径 SERVER_SRC_DIR := ./Server/src SERVER_BUILD_DIR := ./Server/build SERVER_EXE := ./server.exe # 可执行文件放到根目录 # Common模块相关路径 COMMON_SRC_DIR := ./Common/src COMMON_BUILD_DIR := ./Common/build # 收集源码和目标文件 CLIENT_SRCS := $(wildcard $(CLIENT_SRC_DIR)/*.cpp) CLIENT_OBJS := $(patsubst $(CLIENT_SRC_DIR)/%.cpp,$(CLIENT_BUILD_DIR)/%.o,$(CLIENT_SRCS)) SERVER_SRCS := $(wildcard $(SERVER_SRC_DIR)/*.cpp) SERVER_OBJS := $(patsubst $(SERVER_SRC_DIR)/%.cpp,$(SERVER_BUILD_DIR)/%.o,$(SERVER_SRCS)) COMMON_SRCS := $(wildcard $(COMMON_SRC_DIR)/*.cpp) COMMON_OBJS := $(patsubst $(COMMON_SRC_DIR)/%.cpp,$(COMMON_BUILD_DIR)/%.o,$(COMMON_SRCS)) # 伪目标 .PHONY: all clean fclean dirs all: dirs client server # 客户端目标:依赖客户端OBJS和Common OBJS client: $(CLIENT_EXE) $(CLIENT_EXE): $(CLIENT_OBJS) $(COMMON_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) # 服务端目标:依赖服务端OBJS和Common OBJS server: $(SERVER_EXE) $(SERVER_EXE): $(SERVER_OBJS) $(COMMON_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) # 客户端源码编译规则:只依赖对应客户端cpp文件,不需要Common同名文件 $(CLIENT_BUILD_DIR)/%.o: $(CLIENT_SRC_DIR)/%.cpp $(CC) $(CFLAGS) -c $< -o $@ # 服务端源码编译规则:同理 $(SERVER_BUILD_DIR)/%.o: $(SERVER_SRC_DIR)/%.cpp $(CC) $(CFLAGS) -c $< -o $@ # Common模块编译规则 $(COMMON_BUILD_DIR)/%.o: $(COMMON_SRC_DIR)/%.cpp $(CC) $(CFLAGS) -c $< -o $@ # 创建目录的规则:确保build目录存在 dirs: mkdir -p $(CLIENT_BUILD_DIR) $(SERVER_BUILD_DIR) $(COMMON_BUILD_DIR) # 清理目标文件 clean: rm -rf $(CLIENT_BUILD_DIR)/* $(SERVER_BUILD_DIR)/* $(COMMON_BUILD_DIR)/* # 清理所有生成文件 fclean: clean rm -f $(CLIENT_EXE) $(SERVER_EXE) # 重新编译 re: fclean all
关键修改点
- 修复编译规则逻辑:移除了客户端/服务端编译规则中对Common同名文件的强制依赖,每个模块的
.cpp只编译自身对应的.o。 - 添加Common模块的编译:单独处理Common源码的编译,生成对应的
.o文件,并让客户端/服务端的可执行文件依赖这些Common的.o。 - 调整可执行文件路径:将
client.exe和server.exe放到根目录,符合你的需求。 - 改进目录创建:使用
mkdir -p确保目录不存在时自动创建,避免重复创建报错。 - 优化清理规则:只删除目录内的文件而非整个目录,保留目录结构;清理可执行文件时直接删除根目录下的文件。
内容的提问来源于stack exchange,提问作者thibaultk
相关产品推荐
相关产品推荐

