makefile 指定文件的生成目录

本文介绍了一种使用Makefile实现源代码自动化构建的方法,包括如何组织目录结构、自动生成依赖文件及清理过程。通过具体示例详细说明了Makefile中变量定义、依赖文件生成等关键步骤。

1.上一篇博客虽然简单实现了自动处理依赖关系,但是生成的各种临时文件都混在一起,太乱了。

假定我们的源文件放在src目录,头文件放在inc目录,.o文件放在obj目录,.d文件放在dmk目录,Makefile和上述4个目录为同一级别。

则定义如下变量:

D_SRC = src
D_INC = -I./inc
D_OBJ = obj
D_MK  = dmk

2.自动遍历src目录下的所有.c文件

SRC_C   = $(wildcard $(D_SRC)/*.c)
         # wildcard表示把$(D_SRC)目录下的.c文件遍历出来

SRC_C   = $(foreach dir, $(D_SRC), $(wildcard $(dir)/*.c))
       # foreach表示遍历$(D_SRC)的所有子目录同时把子目录下的.c文件遍历出来

假设src目录中有a.c,b.c,则$(SRC_C) 表示 src/a.c src/b.c

3.生成所有的.o文件依赖和.d文件集合

OBJ_C   = $(addprefix $(D_OBJ)/,$(patsubst %.c,%.o,$(notdir $(SRC_C))))
SRC_MK  = $(addprefix $(D_MK)/, $(patsubst %.c,%.d,$(notdir $(SRC_C))))

notdir表示去除目录,则$(notdir $(SRC_C))表示a.c b.c

patsubst表示把$(notdir $(SRC_C))中的.c替换成.o,即a.o b.o

addprefix表示增加前缀$(D_OBJ)/,则OBJ_C变量表示为obj/a.o obj/b.o

4.明白了上述语句之后我们可以把makefile写成如下状态

D_SRC = src
D_INC = -I./inc
D_OBJ = obj
D_MK  = dmk
TATGET = hello

SRC_C   = $(foreach dir, $(D_SRC), $(wildcard $(dir)/*.c))
OBJ_C   = $(addprefix $(D_OBJ)/,$(patsubst %.c,%.o,$(notdir $(SRC_C))))
SRC_MK  = $(addprefix $(D_MK)/, $(patsubst %.c,%.d,$(notdir $(SRC_C))))

$(TATGET):$(OBJ_C)
	gcc -o $@ $^

$(D_OBJ)/%.o:$(D_SRC)/%.c
	gcc -c -Wall $(D_INC) $< -o $@

$(D_MK)/%.d:$(D_SRC)/%.c
	@set -e; rm -f $@; \
	$(CC) -MM $(D_INC) $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,$(D_OBJ)/\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$

include $(SRC_MK)

.PHONY: clean
clean:
	rm -f $(D_OBJ)/* $(TATGET) $(D_MK)/*

多出来的几行依赖关系其实就是一个字符串匹配的模式,clean就不需要解释了。

5.vpath 自动变量的使用,第一个参数是查找的类型,第二个是查找的目录

vpath %.c src #查找依赖时如果遇到%.c,则自动到src目录下寻找
D_SRC = src
D_INC = -I./inc
D_OBJ = obj
D_MK  = dmk
TATGET = hello

SRC_C   = $(foreach dir, $(D_SRC), $(wildcard $(dir)/*.c))
OBJ_C   = $(addprefix $(D_OBJ)/,$(patsubst %.c,%.o,$(notdir $(SRC_C))))
SRC_MK  = $(addprefix $(D_MK)/, $(patsubst %.c,%.d,$(notdir $(SRC_C))))

$(TATGET):$(OBJ_C)
	gcc -o $@ $^

$(D_OBJ)/%.o:%.c  #自动去src目录下找.c结尾的文件。
	gcc -c -Wall $(D_INC) $< -o $@

$(D_MK)/%.d:%.c
	@set -e; rm -f $@; \
	$(CC) -MM $(D_INC) $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,$(D_OBJ)/\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$

include $(SRC_MK)

.PHONY: clean
clean:
	rm -f $(D_OBJ)/* $(TATGET) $(D_MK)/*

查找多个目录用下面的写法

vpath %.c src:src1:src2

大写VPATH只能指定依赖的查找目录,不能指定类型,所以也可以写成

VPATH = src
D_SRC = src
D_INC = -I./inc
D_OBJ = obj
D_MK  = dmk
TATGET = hello

SRC_C   = $(foreach dir, $(D_SRC), $(wildcard $(dir)/*.c))
OBJ_C   = $(addprefix $(D_OBJ)/,$(patsubst %.c,%.o,$(notdir $(SRC_C))))
SRC_MK  = $(addprefix $(D_MK)/, $(patsubst %.c,%.d,$(notdir $(SRC_C))))

$(TATGET):$(OBJ_C)
	gcc -o $@ $^

$(D_OBJ)/%.o:%.c
	gcc -c -Wall $(D_INC) $< -o $@

$(D_MK)/%.d:%.c  #自动去VPATH指定的目录查找,指定多个路径 写成VPATH = src:src1:src2
	@set -e; rm -f $@; \
	$(CC) -MM $(D_INC) $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,$(D_OBJ)/\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$

include $(SRC_MK)

.PHONY: clean
clean:
	rm -f $(D_OBJ)/* $(TATGET) $(D_MK)/*
<think> 我们在Makefile中通常使用通配符或函数来匹配指定目录的C文件。 根据引用[3]中的示例,使用了`$(wildcard *.c)`来匹配当前目录下的所有.c文件。 但是,如果我们需要匹配指定目录(比如src目录)下的所有C文件,我们可以使用`$(wildcard src/*.c)`。 另外,引用[2]中提到,Makefile可以管理项目中的源文件编译,并生成依赖关系。因此,正确地匹配源文件是构建依赖关系的第一步。 具体方法: 1. 使用wildcard函数:该函数用于展开指定模式的文件列表。 例如:`SRCS = $(wildcard src/*.c)` 将匹配src目录下的所有.c文件,并将文件列表赋值给变量SRCS。 2. 如果目录有多个,可以使用多个wildcard,并用空格分隔目录模式。 例如:`SRCS = $(wildcard src1/*.c src2/*.c)` 3. 如果目录有子目录,并且需要递归匹配,可以使用find命令(但注意,在Makefile中直接使用find可能降低可移植性)或者使用shell函数: 例如:`SRCS = $(shell find src -name '*.c')` 但是,使用find命令可能会降低跨平台性。在纯Makefile中,通常使用多个wildcard模式来匹配多级目录,但需要注意wildcard不支持递归匹配。 替代方案:使用通配符匹配多级目录,如`$(wildcard src/*.c src/*/*.c)`,但这样只能匹配固定层级。 4. 引用[5]中提到可以使用函数(如subst)来替换路径。如果我们匹配了文件,然后需要改变输出路径(比如将.c文件输出到obj目录下的.o文件),可以使用函数处理路径。 示例: 假设项目结构如下: project/ Makefile src/ main.c foo.c bar/ bar.c 我们想要匹配src目录及其子目录下的所有.c文件,可以这样写: SRCS = $(wildcard src/*.c src/*/*.c) 但是,这种方法只能匹配两层目录。对于任意深度的目录,我们可以结合使用find(如果环境支持): SRCS = $(shell find src -type f -name '*.c') 考虑到可移植性,如果不希望依赖find,可以编写一个递归的wildcard函数(使用Makefile的函数递归调用): rwildcard = $(foreach d, $(wildcard $1*), $(call rwildcard,$d/,$2) $(filter $(subst *,%,$2), $d)) 然后使用:SRCS = $(call rwildcard, src/, *.c) 但是,这种方法相对复杂。根据实际需求,如果目录层级固定,使用多个wildcard模式更简单;如果需要递归,且系统支持find,那么使用find命令更直接。 注意:引用[2]中提到了通过gcc生成依赖关系,并且使用include引入依赖文件。因此,在匹配了源文件后,我们还需要处理依赖关系。 总结:在Makefile中匹配指定目录的C文件,最直接的方法是使用wildcard函数指定目录,例如`$(wildcard dir/*.c)`。对于多级目录,根据实际情况选择方法。 下面是一个简单的示例Makefile,用于匹配src目录下的所有.c文件(包括子目录,使用find): CC = gcc CFLAGS = -Wall # 使用find命令递归查找src目录下的所有.c文件 SRCS = $(shell find src -name '*.c') # 将.c文件替换为.o文件(在同一级目录结构,但放在build目录下) OBJS = $(patsubst src/%.c, build/%.o, $(SRCS)) TARGET = main all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $@ $^ build/%.o: src/%.c @mkdir -p $(@D) # 创建目标目录 $(CC) $(CFLAGS) -c $< -o $@ clean: rm -rf build $(TARGET) 注意:这个示例中,我们将构建输出放到build目录中,并且使用了模式规则来编译每个源文件。 但是,引用[5]提到了一种使用subst函数的方法来替换路径。上面的示例中我们使用了patsubst函数来替换路径。 根据用户需求,我们只需要匹配指定目录的C文件,以上方法均可。 考虑到引用[4]中提到了批处理文件的一些变量,但这主要是在Windows批处理中,而Makefile通常运行在Unix-like系统,所以这里不展开。 因此,针对用户问题,我们可以给出以下答案: </think> 在 Makefile 中匹配指定目录的 C 文件主要通过通配符和路径处理函数实现,以下是具体方法及示例: ### 方法一:基础通配符匹配 使用 `wildcard` 函数匹配特定目录下的所有 `.c` 文件: ```makefile # 匹配 src 目录下所有 .c 文件 SRC_DIR = src SRCS = $(wildcard $(SRC_DIR)/*.c) # 示例输出:src/main.c src/utils.c ... ``` ### 方法二:递归匹配子目录 通过 `find` 命令递归匹配目录及其子目录: ```makefile # 递归匹配 src 及其子目录下的 .c 文件 SRCS = $(shell find $(SRC_DIR) -name '*.c') # 示例输出:src/main.c src/utils.c src/sub/module.c ... ``` ### 方法三:多目录匹配 组合多个目录路径: ```makefile # 同时匹配 src 和 lib 目录下的 .c 文件 SRCS = $(wildcard src/*.c) $(wildcard lib/*.c) ``` ### 方法四:高级路径处理 结合 `patsubst` 替换扩展名(常用于生成 `.o` 文件): ```makefile OBJ_DIR = obj OBJS = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRCS)) ``` ### 完整示例 Makefile ```makefile CC = gcc CFLAGS = -Wall SRC_DIR = src OBJ_DIR = obj # 递归匹配所有 .c 文件 SRCS = $(shell find $(SRC_DIR) -name '*.c') OBJS = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRCS)) TARGET = app $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $@ $^ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c @mkdir -p $(@D) # 自动创建 obj 子目录 $(CC) $(CFLAGS) -c $< -o $@ clean: rm -rf $(OBJ_DIR) $(TARGET) ``` ### 关键说明: 1. **`wildcard` 函数** 用于静态展开文件列表(Makefile 解析阶段确定)[^3]。 2. **`$(shell find ...)`** 动态获取文件列表(支持递归搜索),但需注意跨平台兼容性[^2]。 3. **路径处理技巧** - `$(@D)` 自动获取目标文件目录路径 - `patsubst` 实现路径转换(`.c` → `.o` 并更改目录)[^5] 4. **依赖管理** 建议结合 `gcc -MM` 自动生成文件依赖(如引用[2]所述) > 提示:若需处理深层嵌套目录,推荐结合 `find` + `patsubst` 确保路径准确性[^5]。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值