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)
修复说明
- 统一收集源文件:一次性调用
find获取所有.f90文件,生成对应的.o文件列表,避免重复操作 - 正确生成规则:使用
eval为每个源文件创建独立规则,将编译命令放在规则的命令块中,确保Make能正确识别每个目标的依赖和构建命令 - 添加目录依赖:通过
| $(BUILD)确保编译前build目录已存在,避免因目录缺失导致编译失败 - 简化默认目标:直接将
all目标依赖所有.o文件,触发所有规则的执行 - 添加清理规则:方便快速清理生成的目标文件
内容的提问来源于stack exchange,提问作者TobiR
相关产品推荐
相关产品推荐

