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

如何编写Makefile使$(OBJS)在generate-src执行后再求值?

问题描述

我有一个包含生成源文件规则的Makefile(简化示例):

SRCS = $(shell find src -name "*.c")
OBJS = $(patsubst src/%.c, build/%.o, $(SRCS))

generate-src:
    @echo "int main() {return 0;}" > ./src/x.c

target: generate-src $(OBJS)
    $(CC) -o target $(OBJS)

build/%.o: src/%.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $(CXXFLAGS) -c -o $@ $<

执行make target时出现如下错误:

cc -o target  build/x.o
clang: error: no such file or directory: 'build/x.o'
clang: error: no input files
make: *** [target] Error 1

问题原因:执行make target时,$(SRCS)会在Makefile解析阶段就通过shell命令求值,此时src/x.c还未生成,导致$(SRCS)为空、$(OBJS)也为空;随后执行generate-src规则创建了src/x.c,但链接命令里的$(OBJS)此时被替换为build/x.o,可这个目标文件并没有被加入target的依赖列表,所以不会触发编译规则生成它。

请问如何编写该Makefile,让$(OBJS)在generate-src执行完成后再进行求值?

解决方案

方法1:使用递归Make触发二次解析

让target先完成源文件生成,再递归调用Make重新解析变量,此时新生成的源文件已存在,SRCS能正确获取:

target: generate-src
	@$(MAKE) --no-print-directory real-target

real-target: $(OBJS)
	$(CC) -o target $(OBJS)

SRCS = $(shell find src -name "*.c")
OBJS = $(patsubst src/%.c, build/%.o, $(SRCS))

generate-src:
	@echo "int main() {return 0;}" > ./src/x.c

build/%.o: src/%.c
	@mkdir -p $(@D)
	$(CC) $(CFLAGS) $(CXXFLAGS) -c -o $@ $<

原理:第一次Make仅执行generate-src生成文件,随后启动新的Make进程重新解析整个Makefile,此时find命令能找到刚生成的src/x.c,进而正确生成OBJS并完成编译链接。

方法2:使用延迟展开变量+强制依赖扫描

将SRCS和OBJS设为延迟展开的普通变量(用=而非:=),并在生成文件后触发Make重新扫描依赖:

SRCS = $(shell find src -name "*.c")
OBJS = $(patsubst src/%.c, build/%.o, $(SRCS))

generate-src:
	@echo "int main() {return 0;}" > ./src/x.c
	@touch src  # 更新源目录时间戳,让Make重新扫描src下的文件

target: generate-src
	$(MAKE) $(OBJS)
	$(CC) -o target $(OBJS)

build/%.o: src/%.c
	@mkdir -p $(@D)
	$(CC) $(CFLAGS) $(CXXFLAGS) -c -o $@ $<

原理:延迟展开变量会在每次使用时重新求值,touch src会让Make认为源目录有变化,触发对src/*.c的重新扫描,确保OBJS能获取到新生成的文件,再单独编译目标文件后链接。

方法3:显式指定生成的源文件(适合已知生成文件名的场景)

如果预先知道要生成的源文件名,直接将其加入依赖链,避免动态查找带来的求值时机问题:

GENERATED_SRC = src/x.c
OBJS = build/x.o

generate-src: $(GENERATED_SRC)

$(GENERATED_SRC):
	@echo "int main() {return 0;}" > $@

target: $(OBJS)
	$(CC) -o target $(OBJS)

build/%.o: src/%.c
	@mkdir -p $(@D)
	$(CC) $(CFLAGS) $(CXXFLAGS) -c -o $@ $<

这种方式最直接,完全规避了动态查找变量的求值时机问题,适合生成文件固定的场景。

内容的提问来源于stack exchange,提问作者pdid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 08:10:03