GNU Make能否实现部分目标并行、其余目标串行执行?
核心结论
GNU Make 完全支持「部分目标并行执行、其余目标固定串行」的执行模式,不需要完全关闭-j带来的并行编译能力。你提供的两个示例本身存在规则方向颠倒的问题,且没有用到Make针对单类目标的串行控制能力,无法满足需求。
现有示例的问题点
- 规则依赖方向完全颠倒:无论显式规则还是模式规则,正常编译逻辑是「由源文件生成目标文件」,即依赖关系应该是目标文件(.o)依赖对应的源文件(.c/.s)。你当前写的
$(c_sources) : $(c_objs)、%.c : %.o的逻辑,是让Make基于已有的.o文件反向生成.c/.s源文件,完全不符合正常编译流程,哪怕不开并行也无法正确执行编译动作。 - 没有用到分目标串行的控制机制:之前尝试的全局
.NOTPARALLEL写在Makefile顶层时,会让整个Make流程全部串行,和你的需求相反;之前查阅到的顺序编排方案,只是用.ORDER这类目标保证依赖的先后执行顺序,无法阻止多个目标同时并行运行。
正确实现方案
核心思路是利用GNU Make 3.82及以上版本支持的作用域化的.NOTPARALLEL特性:当.NOTPARALLEL作为某一个聚合目标的前置依赖时,Make在构建这个聚合目标的整个依赖子图时,会临时关闭并行执行模式,子图内所有目标严格串行执行;这个子图之外的其他目标不受影响,可以正常利用-j参数并行运行。
很多用户之前尝试这个特性失败,是因为将
.NOTPARALLEL直接写在Makefile全局最前端,这种写法会让整个Make构建流程全部关闭并行,完全达不到分目标控制的效果。只有将.NOTPARALLEL作为专门聚合串行组的目标的依赖写入,才能实现作用域隔离。
显式声明源文件规则的正确写法
# 可被并行执行的C编译目标 c_sources := a.c b.c c.c c_objs := a.o b.o c.o # 必须串行执行的汇编目标 assembly_sources := d.s e.s f.s assembly_objs := d.o e.o f.o # 全局总目标 all: $(c_objs) assembly_phase # 专门聚合所有汇编目标的阶段目标,通过前置.NOTPARALLEL标记 # 该目标下的所有依赖会强制串行执行 assembly_phase: .NOTPARALLEL $(assembly_objs) # C文件编译规则:不在串行子图内,可正常并行 $(c_objs): %.o: %.c @echo 'Command used to compile .c files.' # 汇编文件编译规则:被assembly_phase覆盖,强制串行 $(assembly_objs): %.o: %.s @echo 'Command used to assemble assembly files (.s files).'
执行时哪怕携带-j8参数:
- 所有a.o/b.o/c.o的C编译动作,会共享并行槽位同时运行
- 所有d.o/e.o/f.o的汇编动作,哪怕并行槽位充足,也会严格一个接一个串行执行,不会同时启动多个汇编进程
模式规则实现的正确写法
逻辑和显式规则完全一致,只需要通过变量标识哪些汇编目标属于串行组即可:
# 标记需要串行的汇编目标列表 assembly_objs := d.o e.o f.o # 可并行的C编译目标 c_objs := a.o b.o c.o all: $(c_objs) assembly_phase # 给汇编阶段加作用域化的NOTPARALLEL标记 assembly_phase: .NOTPARALLEL $(assembly_objs) # C编译模式规则:正常并行 %.o: %.c @echo 'Command used to compile .c files.' # 汇编编译模式规则:被assembly_phase覆盖后强制串行 %.o: %.s @echo 'Command used to assemble assembly files (.s files).'
老版本GNU Make的兼容方案
如果使用的Make版本低于3.82,不支持作用域化的.NOTPARALLEL,可以通过递归Make的方式实现等价效果:
- 把C文件的并行编译放在独立的递归Make调用里,携带
-j8参数执行 - 把汇编部分的编译作为单独的Make调用,不携带
-j参数,强制串行 - 主Makefile只负责编排两个阶段的先后顺序,不需要自己处理并行逻辑
内容的提问来源于stack exchange,提问作者mAshraf
相关产品推荐
相关产品推荐

