这是我添加到文档中的 Makefile(目前正在审核,所以我会在这里发布):
PROJDIR := $(realpath $(CURDIR)/..)
SOURCEDIR := $(PROJDIR)/Sources
BUILDDIR := $(PROJDIR)/Build
TARGET = myApp.exe
VERBOSE = TRUE
DIRS = Folder0 Folder1 Folder2
SOURCEDIRS = $(foreach dir, $(DIRS), $(addprefix $(SOURCEDIR)/, $(dir)))
TARGETDIRS = $(foreach dir, $(DIRS), $(addprefix $(BUILDDIR)/, $(dir)))
INCLUDES = $(foreach dir, $(SOURCEDIRS), $(addprefix -I, $(dir)))
VPATH = $(SOURCEDIRS)
SOURCES = $(foreach dir,$(SOURCEDIRS),$(wildcard $(dir)/*.c))
OBJS := $(subst $(SOURCEDIR),$(BUILDDIR),$(SOURCES:.c=.o))
DEPS = $(OBJS:.o=.d)
CC = gcc
ifeq ($(OS),Windows_NT)
RM = del /F /Q
RMDIR = -RMDIR /S /Q
MKDIR = -mkdir
ERRIGNORE = 2>NUL || true
SEP=\\
else
RM = rm -rf
RMDIR = rm -rf
MKDIR = mkdir -p
ERRIGNORE = 2>/dev/null
SEP=/
endif
PSEP = $(strip $(SEP))
ifeq ($(VERBOSE),TRUE)
HIDE =
else
HIDE = @
endif
define generateRules
$(1)/%.o: %.c
@echo Building $$@
$(HIDE)$(CC) -c $$(INCLUDES) -o $$(subst /,$$(PSEP),$$@) $$(subst /,$$(PSEP),$$<) -MMD
endef
.PHONY: all clean directories
all: directories $(TARGET)
$(TARGET): $(OBJS)
$(HIDE)echo Linking $@
$(HIDE)$(CC) $(OBJS) -o $(TARGET)
-include $(DEPS)
$(foreach targetdir, $(TARGETDIRS), $(eval $(call generateRules, $(targetdir))))
directories:
$(HIDE)$(MKDIR) $(subst /,$(PSEP),$(TARGETDIRS)) $(ERRIGNORE)
clean:
$(HIDE)$(RMDIR) $(subst /,$(PSEP),$(TARGETDIRS)) $(ERRIGNORE)
$(HIDE)$(RM) $(TARGET) $(ERRIGNORE)
@echo Cleaning done !
主要特点
- 自动检测指定文件夹中的源文件
- 多个源文件夹
- 多个相应的目标文件和依赖项文件的目标文件夹
- 为每个目标文件夹自动生成规则
- 当目标文件夹不存在时创建它们
- 使用
gcc
进行依赖管理:仅构建必要的内容
- 适用于
Unix
和DOS
系统
- 编写于
GNU Make
之上
如何使用此Makefile
要将此Makefile适应到您的项目中,您需要执行以下操作:
- 将
TARGET
变量更改为与您的目标名称匹配
- 在
SOURCEDIR
和BUILDDIR
中更改Sources
和Build
文件夹的名称
- 在Makefile本身或make调用中更改Makefile的详细程度(
make all VERBOSE=FALSE
)
- 将
DIRS
中的文件夹名称更改为与您的源文件夹和构建文件夹匹配的名称
- 如果需要,更改编译器和标志
在此Makefile中,Folder0
、Folder1
和Folder2
相当于您的FolderA
、FolderB
和FolderC
。
请注意,我目前尚未有机会在Unix系统上进行测试,但它在Windows上能够正常工作。
一些棘手部分的解释:
忽略Windows mkdir错误
ERRIGNORE = 2>NUL || true
这有两个影响:
第一个影响是,2>NUL
将错误输出重定向到 NUL,因此不会在控制台中显示。
第二个影响是,|| true
防止命令提高错误级别。这是与 Makefile 无关的 Windows 东西,它在这里是因为 Windows 的 mkdir
命令如果我们试图创建一个已经存在的文件夹会提高错误级别,而实际上我们并不关心,如果它已经存在那也没关系。常见的解决方案是使用 if not exist
结构,但这不符合 UNIX 兼容性,所以尽管有些棘手,我认为我的解决方案更清晰。
创建 OBJS 变量,其中包含所有对象文件及其正确的路径
OBJS := $(subst $(SOURCEDIR),$(BUILDDIR),$(SOURCES:.c=.o))
在这里,我们希望OBJS包含所有对象文件及其路径,并且我们已经有了SOURCES,其中包含所有源文件及其路径。
$(SOURCES:.c=.o)
将*.c更改为所有源的*.o,但路径仍然是源路径。
$(subst $(SOURCEDIR),$(BUILDDIR), ...)
将简单地用生成路径减去整个源路径,因此我们最终拥有一个变量,其中包含带有其路径的.o文件。
处理Windows和Unix风格的路径分隔符
SEP=\\
SEP = /
PSEP = $(strip $(SEP))
这仅存在是为了允许Makefile在Unix和Windows上工作,因为Windows在路径中使用反斜杠而其他操作系统使用斜杠。
SEP=\\
在这里双斜杠用于转义反斜杠字符,这可以避免 make
将其解释为“忽略换行符”,从而允许跨多行编写。
PSEP = $(strip $(SEP))
这将去除 SEP
变量自动添加的空格字符。
为每个目标文件夹自动生成规则
define generateRules
$(1)/%.o: %.c
@echo Building $$@
$(HIDE)$(CC) -c $$(INCLUDES) -o $$(subst /,$$(PSEP),$$@) $$(subst /,$$(PSEP),$$<) -MMD
endef
这可能是与您使用场景最相关的技巧。这是一个规则模板,可以使用 $(eval $(call generateRules, param))
生成,其中 param
是模板中的 $(1)
。这将为每个目标文件夹填充 Makefile 规则,例如:
path/to/target/%.o: %.c
@echo Building $@
$(HIDE)$(CC) -c $(INCLUDES) -o $(subst /,$(PSEP),$@) $(subst /,$(PSEP),$<) -MMD