如何拆分GNU Make的shell函数调用为多行?报错原因咨询
问题原因拆解
你遇到的这个“报错但文件生成正常、二次执行无报错”的现象,核心是**$(shell)函数的输出被Make当成了Makefile语法内容**,而非单纯的shell命令执行问题:
第一次执行报错的逻辑
你直接把$(shell ...)写在Makefile的一行里,没有赋值给任何变量。$(shell)的特性是会捕获shell命令的标准输出,并将输出直接插入到Makefile的当前位置:
- 第一次执行时,
.oldmodel不存在,shell的if条件成立,执行了echo $(MODEL),这个echo的输出(也就是你设置的MODEL值,比如resnet50)会被$(shell)返回,相当于Makefile在这一行插入了resnet50。 - 而Makefile语法中,单独的字符串会被识别为目标名称,但目标后面必须跟冒号
:定义依赖或命令,你这里没有,所以Make抛出了missing separator的语法错误。 - 注意:shell命令是在Make解析这一行之前就执行了的,所以
.oldmodel文件已经被正确生成,只是Make处理$(shell)输出时出了语法问题。
第二次执行无报错的逻辑
第二次执行时,.oldmodel已存在且内容和MODEL一致,shell的if条件不成立,echo $(MODEL)不会执行,$(shell)函数的输出是空字符串。这相当于Makefile的这一行是空行,Make不会做语法检查,自然就没有报错。
正确写法推荐
针对这种“只需要执行命令、不需要捕获输出”的场景,有两种规范的解决方式:
方式1:用临时变量接收$(shell)输出
把$(shell)的输出赋值给一个无关变量,避免输出被当成Makefile语法内容:
# 用dummy变量接收输出,避免干扰Makefile解析 _DUMMY := $(shell if [ ! -f .oldmodel ] || [ "$(MODEL)" != `cat .oldmodel` ] ; then echo $(MODEL) > .oldmodel ; fi )
方式2:改用Make规则命令执行(更推荐)
用伪目标定义命令逻辑,符合Makefile的规范写法:
# 定义伪目标,避免和实际文件重名 .PHONY: update-oldmodel update-oldmodel: @if [ ! -f .oldmodel ] || [ "$(MODEL)" != `cat .oldmodel` ]; then \ echo $(MODEL) > .oldmodel; \ fi # 在需要触发检查的目标中依赖这个伪目标 your-target: update-oldmodel # 你的其他构建命令...
这里@是为了不让Make打印出if命令本身;每行末尾的\是shell的换行转义,告诉shell命令未结束。
内容的提问来源于stack exchange,提问作者yuanjianpeng
相关产品推荐
相关产品推荐

