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

Makefile中用foreach实现子目录顺序编译仅编译首个文件的问题

Makefile foreach递归编译仅生成第一个目标的修复方案

问题场景

编写了如下Makefile,意图通过foreach编译PATH_POT3目录下所有子目录的Fortran源码,但实际执行时仅编译第一个目标文件,其余均被跳过。

原Makefile代码

BUILD = build
PATH_POT3 = src/pot/pot3/
MPIFC = mpiifort
FFLAGS = -O3

MOL_LIST = $(shell find $(PATH_POT3) -mindepth 1 -maxdepth 1 -type d)

$(foreach MOL,$(MOL_LIST), \
  $(eval LIST_MOL := $(shell find $(MOL) -name "*.f90")) \
  $(info Fortran files: $(LIST_MOL)) \
  $(foreach SRC,$(LIST_MOL), \
    $(eval OBJ := $(BUILD)/$(notdir $(SRC:.f90=.o))) \
    $(OBJ): $(SRC) ; \
    $(info Compiling: $(SRC)) \
    $(info Output: $(OBJ)) \
    $(MPIFC) $(FFLAGS) -c $(SRC) -o $(OBJ) \
  ) \
)

all: $(foreach MOL,$(MOL_LIST),\
    $(foreach SRC,$(shell find $(MOL) -name "*.f90"),\
        $(BUILD)/$(notdir $(SRC:.f90=.o))\
    )\
)

执行输出

Fortran files: src/pot/pot3/ar3/pot_ar3.f90 src/pot/pot3/ar3/ar43/pot_ar43.f90
Compiling: mpiifort -O3 -c src/pot/pot3/ar3/pot_ar3.f90 -o build/pot_ar3.o
Output: build/pot_ar3.o
Compiling: mpiifort -O3 -c src/pot/pot3/ar3/ar43/pot_ar43.f90 -o build/pot_ar43.o
Output: build/pot_ar43.o
Fortran files: src/pot/pot3/ar33/pot_ar33.f90 src/pot/pot3/ar33/pot_ar333.f90 src/pot/pot3/ar33/ar53/pot_ar53.f90
Compiling: mpiifort -O3 -c src/pot/pot3/ar33/pot_ar33.f90 -o build/pot_ar33.o
Output: build/pot_ar33.o
Compiling: mpiifort -O3 -c src/pot/pot3/ar33/pot_ar333.f90 -o build/pot_ar333.o
Output: build/pot_ar333.o
Compiling: mpiifort -O3 -c src/pot/pot3/ar33/ar53/pot_ar53.f90 -o build/pot_ar53.o
Output: build/pot_ar53.o
make: 'build/pot_ar3.o' is up to date.

问题原因

原Makefile的核心问题是:

  • 在foreach循环中直接编写编译命令,导致这些命令在Makefile解析阶段就被执行,而非作为目标规则的命令部分
  • 规则定义语法错误:$(OBJ): $(SRC) ;后直接跟编译命令的写法,未正确将命令关联到目标,后续目标的规则未被Make正确识别
  • 重复调用find命令,既低效又可能导致依赖关系处理混乱

修复后的Makefile

BUILD = build
PATH_POT3 = src/pot/pot3/
MPIFC = mpiifort
FFLAGS = -O3

# 一次性收集所有Fortran源文件,避免重复调用find
ALL_SRCS = $(shell find $(PATH_POT3) -name "*.f90")
# 生成对应的目标文件路径
ALL_OBJS = $(patsubst %.f90,$(BUILD)/%.o,$(notdir $(ALL_SRCS)))

# 确保BUILD目录存在
$(BUILD):
	mkdir -p $@

# 为每个源文件生成编译规则
$(foreach SRC,$(ALL_SRCS), \
	$(eval OBJ := $(BUILD)/$(notdir $(SRC:.f90=.o))) \
	$(eval $(OBJ): $(SRC) | $(BUILD) ; \
		$(MPIFC) $(FFLAGS) -c $< -o $@ \
	) \
)

# 默认目标:编译所有文件
all: $(ALL_OBJS)

# 清理目标
clean:
	rm -rf $(BUILD)

修复说明

  1. 统一收集源文件:一次性调用find获取所有.f90文件,生成对应的.o文件列表,避免重复操作
  2. 正确生成规则:使用eval为每个源文件创建独立规则,将编译命令放在规则的命令块中,确保Make能正确识别每个目标的依赖和构建命令
  3. 添加目录依赖:通过| $(BUILD)确保编译前build目录已存在,避免因目录缺失导致编译失败
  4. 简化默认目标:直接将all目标依赖所有.o文件,触发所有规则的执行
  5. 添加清理规则:方便快速清理生成的目标文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:03:25