You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Make识别目录?通用Makefile规则与输出目录配置问询

针对你遇到的Makefile编写痛点——手动写依赖太繁琐、目标文件管理混乱且重复编译的问题,结合你的目录结构,我给你整理一套实用的解决方案:

首先先明确你的目录结构:

source/
|-Makefile
|-a.c
|-b.c
|-c.c
include/
|-arch1/
|-arch2/
|-common/
|-arch1-c_files/
1. 用通配符编写通用规则 + 自动生成头文件依赖

手动枚举每个.c对应的头文件确实效率极低,咱们可以借助GCC的自动依赖生成功能,搭配Make的通配符特性来实现通用规则,彻底解放双手。

先在source/Makefile里定义基础变量,把通用配置抽离出来:

# 配置头文件搜索路径,覆盖include下所有子目录
CFLAGS := -I../include -I../include/arch1 -I../include/arch2 -I../include/common -I../include/arch1-c_files
# 定义目标文件输出目录,比如统一放到上级目录的obj文件夹里
OBJDIR := ../obj
# 自动扫描source目录下所有.c文件
SRCS := $(wildcard *.c)
# 把每个.c文件映射到OBJDIR下对应的.i文件
TARGETS := $(patsubst %.c,$(OBJDIR)/%.i,$(SRCS))

接下来编写通用规则,同时自动生成依赖文件(避免手动维护头文件依赖):

# 确保输出目录存在,不存在就创建
$(OBJDIR):
	mkdir -p $@

# 通用的%.i生成规则:匹配所有.c文件,生成对应路径的.i文件
$(OBJDIR)/%.i: %.c | $(OBJDIR)
	# 生成预处理文件(.i)
	gcc $(CFLAGS) -E $< -o $@
	# 用GCC自动生成该.c文件的头文件依赖,保存到.d文件
	gcc $(CFLAGS) -MM $< > $(OBJDIR)/$*.d
	# 修正依赖文件里的目标路径(因为我们把.i放到了OBJDIR,不是当前目录)
	sed -i 's/^\(.*\)\.o:/$(OBJDIR)\/\1.i:/' $(OBJDIR)/$*.d

# 引入所有自动生成的.d依赖文件,Make会自动识别头文件变化
-include $(patsubst %.c,$(OBJDIR)/%.d,$(SRCS))

# 默认目标:生成所有.i文件
all: $(TARGETS)

这样做的核心优势:

  • 不用手动写任何头文件依赖,GCC会自动扫描#include的所有文件并生成依赖关系
  • 通配符规则$(OBJDIR)/%.i: %.c会自动匹配所有source下的.c文件,无需逐个编写规则
2. 目标文件分目录存放 + 避免重复编译

上面的规则已经把所有.i文件统一放到../obj目录下,Make的核心机制就是对比源文件/依赖文件和目标文件的修改时间——只有当源文件(或它依赖的头文件)比目标文件新时,才会重新编译,完全避免不必要的重复编译。

如果你的需求是把不同的.c文件输出到不同的子目录(比如按架构划分,a.c输出到obj/arch1,c.c输出到obj/arch2),可以调整规则如下:

# 按架构划分输出目录
OBJDIR_ARCH1 := ../obj/arch1
OBJDIR_ARCH2 := ../obj/arch2
# 手动划分不同架构对应的源文件(可根据实际情况调整)
SRCS_ARCH1 := a.c b.c
SRCS_ARCH2 := c.c
# 映射对应架构的.i目标文件
TARGETS_ARCH1 := $(patsubst %.c,$(OBJDIR_ARCH1)/%.i,$(SRCS_ARCH1))
TARGETS_ARCH2 := $(patsubst %.c,$(OBJDIR_ARCH2)/%.i,$(SRCS_ARCH2))

# 创建所有需要的输出目录
$(OBJDIR_ARCH1) $(OBJDIR_ARCH2):
	mkdir -p $@

# arch1专属的通用规则
$(OBJDIR_ARCH1)/%.i: %.c | $(OBJDIR_ARCH1)
	gcc $(CFLAGS) -E $< -o $@
	gcc $(CFLAGS) -MM $< > $(OBJDIR_ARCH1)/$*.d
	sed -i 's/^\(.*\)\.o:/$(OBJDIR_ARCH1)\/\1.i:/' $(OBJDIR_ARCH1)/$*.d

# arch2专属的通用规则
$(OBJDIR_ARCH2)/%.i: %.c | $(OBJDIR_ARCH2)
	gcc $(CFLAGS) -E $< -o $@
	gcc $(CFLAGS) -MM $< > $(OBJDIR_ARCH2)/$*.d
	sed -i 's/^\(.*\)\.o:/$(OBJDIR_ARCH2)\/\1.i:/' $(OBJDIR_ARCH2)/$*.d

# 引入所有依赖文件
-include $(patsubst %.c,$(OBJDIR_ARCH1)/%.d,$(SRCS_ARCH1))
-include $(patsubst %.c,$(OBJDIR_ARCH2)/%.d,$(SRCS_ARCH2))

# 默认目标:生成所有架构的.i文件
all: $(TARGETS_ARCH1) $(TARGETS_ARCH2)

现在,不同架构的源文件会输出到各自的目录中,而且只有当某个.c文件或它依赖的头文件有更新时,才会重新生成对应的.i文件,未修改的文件不会重复编译。

内容的提问来源于stack exchange,提问作者Vishwajith.K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:28:45