如何防止Make Recipe在前置Make命令完成前执行(Hugo构建场景)
解决Make Recipe并发执行冲突的方案
要避免Make Recipe在前一次还没跑完就被重复触发,最靠谱的办法是用文件锁机制——通过一个专属的锁文件,确保同一时间只有一个构建进程在运行,具体实现如下:
1. 基础版锁机制
修改你的Makefile规则,在执行核心逻辑前先检查锁文件:
# 定义锁文件路径,选个不会和现有文件重名的即可 LOCK_FILE := .hugo-build.lock test: # 检查锁文件是否存在,存在则直接退出 @if [ -f $(LOCK_FILE) ]; then \ printf "Error: 上一次构建仍在运行,本次执行退出\n"; \ exit 1; \ fi # 创建锁文件,占住"唯一执行"的坑位 @touch $(LOCK_FILE) # 这里替换成你的Hugo构建核心逻辑 @printf "Start run of make recipe at $$(date)\n" > demo.txt @sleep 30s @printf "\nFinish run of make recipe at $$(date)" >> demo.txt # 执行完成后删除锁文件,释放坑位 @rm -f $(LOCK_FILE)
2. 增强版:处理意外中断情况
如果构建中途意外挂掉(比如进程被强行终止),锁文件会残留导致后续构建无法执行。可以给锁文件加入进程ID,执行前先验证对应进程是否真的存活:
LOCK_FILE := .hugo-build.lock test: @if [ -f $(LOCK_FILE) ]; then \ # 读取锁文件中的进程ID,检查进程是否存活 PID=$$(cat $(LOCK_FILE)); \ if ps -p $$PID > /dev/null 2>&1; then \ printf "Error: 上一次构建(进程ID: $$PID)仍在运行,本次退出\n"; \ exit 1; \ else \ printf "发现残留锁文件,对应进程已终止,先清理锁文件\n"; \ rm -f $(LOCK_FILE); \ fi \ fi # 将当前进程ID写入锁文件 @echo $$ > $(LOCK_FILE) # 核心构建逻辑 @printf "Start run of make recipe at $$(date)\n" > demo.txt @sleep 30s @printf "\nFinish run of make recipe at $$(date)" >> demo.txt # 执行完成后删除锁文件 @rm -f $(LOCK_FILE)
适配Git Hook场景
因为你的构建由Git Hook触发,建议把锁文件放在仓库根目录,但不要提交到Git——将锁文件名添加到.gitignore即可,确保所有Hook触发的构建都能检测到同一个锁文件。
另外注意:你之前的测试规则末尾加了&让命令后台运行,这个必须去掉!后台运行会让Make误以为命令已执行完成,锁文件会被立即删除,锁机制就失效了。
为啥之前的作业管理/wait没用?
你尝试的作业管理或wait命令解决不了问题,是因为每次make test都是独立进程,它们的作业列表相互隔离,无法感知彼此的运行状态。而文件锁是跨进程的共享状态标识,能有效阻挡并发执行请求。
内容的提问来源于stack exchange,提问作者ryanjamurphy
相关产品推荐
相关产品推荐

