所以我有一个GNU Makefile,可以将Game目录及其子目录中的所有.cpp文件编译到build/obj文件夹中的.o文件中(该文件夹中没有子目录(。我的问题是,只编译了列表中的第一个文件,我知道问题是我需要一个构建模式,但我想不出在每个子目录中实现这一点的方法。我尝试了几件事,使用foreach循环浏览所有目录,从目录中删除文件名并放置通配符,但都无济于事。有什么帮助吗?
SRC_DIR=src
BUILD_DIR=build
OBJ_DIR=$(BUILD_DIR)/obj
GAME_DIR=$(SRC_DIR)/Game
GAME_DIRS=$(GAME_DIR)/Classes $(GAME_DIR)/ConVars $(GAME_DIR)/EventListener $(GAME_DIR)/Functions $(GAME_DIR)/Interfaces $(GAME_DIR)/Modules $(GAME_DIR)/NetVars $(GAME_DIR)/Offsets $(GAME_DIR)
SRC_FILES=$(foreach dir,$(GAME_DIRS),$(wildcard $(dir)/*.cpp))
OBJ_FILES=$(addprefix $(OBJ_DIR)/,$(notdir $(patsubst %.cpp,%.o,$(SRC_FILES))))
all: $(OUT)
$(OUT): $(OBJ_FILES)
$(CC) -shared $^ -o $@ -std=c++17 -lgdi32
$(OBJ_FILES): $(OBJ_DIR)/%.o: $(SRC_FILES)
$(CC) -c $< $(CFLAGS) -o $@
这是错误的:
$(OBJ_FILES): $(OBJ_DIR)/%.o: $(SRC_FILES)
它说,每个对象文件都依赖于ALL源文件。SRC_FILES
变量每次都扩展到相同的文件列表,因此对于每个对象文件,先决条件列表都是相同的,因此每次编译它时都使用扩展到第一个先决条件的$<
,因此会反复编译相同的文件。
在模式规则中,与模式匹配的名称部分在目标和先决条件之间必须相同,与模式不匹配的部分必须是静态的。
因此,没有办法使用一个单一的模式规则来构建这样的所有源文件。你有很多选择:
您可以创建多个模式规则,每个源目录一个(在这种情况下更容易避免静态模式规则(:
$(OBJ_DIR)/%.c : Classes/%.cpp
...
$(OBJ_DIR)/%.c : ConVars/%.cpp
...
或者,可以将对象文件放入OBJ_DIR的子目录中,但使用相同的前缀。因此,它将生成obj/Classes/class.o
,而不是Classes/class.cpp
生成obj/class.o
。为此,您可以使用:
OBJ_FILES=$(addprefix $(OBJ_DIR)/,$(patsubst %.cpp,%.o,$(SRC_FILES)))
$(OBJ_FILES): $(OBJ_DIR)/%.o : %.cpp
@mkdir -p $(@D)
$(CC) -c $< $(CFLAGS) -o $@
(注意,使用CC
和CFLAGS
构建C++代码是不对的;这些变量传统上用于构建C代码。CXX
和CXXFLAGS
是构建C++代码的最佳变量(
另一种选择是使用VPATH,如下所示:
VPATH = $(GAME_DIRS)
$(OBJ_FILES): $(OBJ_DIR)/%.o : %.cpp
$(CC) -c $< $(CFLAGS) -o $@