如何在Makefile规则中使用.ONESHELL为变量赋值?解决venvPath为空问题
问题解决:Makefile中.ONESHELL下变量为空的问题
问题描述
我编写了如下Makefile配方,想要通过$(eval)设置venvPath变量,但该变量始终为空,希望正确使用.ONESHELL实现需求,或寻找其他解决方法:
.ONESHELL: misc: $(eval venvPath := $(shell xdg-user-dir ENVIRONMENTS)/nav) ifeq ($(wildcard ${venvPath}),) python -m venv ${venvPath} \ && source ${venvPath}/bin/activate \ && pip install -U pip wheel source ${venvPath}/bin/activate \ && pip install powerline-status endif .PHONY: misc
原因分析
核心问题是Make的解析顺序逻辑:
ifeq属于Make的原生条件判断,在解析Makefile的阶段就会执行,此时recipe里的$(eval)还未运行($(eval)是在目标执行阶段才会触发);.ONESHELL仅作用于shell命令,让目标下的所有命令在同一个shell进程中执行,但不改变Make自身的变量解析逻辑,所以ifeq判断时venvPath还未被赋值,自然为空。
解决方案
方案一:提前在解析阶段定义变量
直接在目标外部用shell函数获取路径,这样venvPath会在Make解析阶段就被赋值,ifeq可以正常判断:
.ONESHELL: # 解析阶段就获取路径,赋值给venvPath venvPath := $(shell xdg-user-dir ENVIRONMENTS)/nav misc: ifeq ($(wildcard ${venvPath}),) python -m venv ${venvPath} \ && source ${venvPath}/bin/activate \ && pip install -U pip wheel \ && pip install powerline-status endif .PHONY: misc
这里将两个source步骤合并成一条命令,避免重复激活虚拟环境。
方案二:用shell的条件判断替代Make的ifeq
既然用了.ONESHELL,可以直接在shell层面处理条件判断,完全绕开Make的变量解析顺序问题:
.ONESHELL: misc: # 在shell中定义变量,用$${}避免被Make解析 venvPath=$(xdg-user-dir ENVIRONMENTS)/nav if [ ! -d "$${venvPath}" ]; then python -m venv "$${venvPath}" \ && source "$${venvPath}/bin/activate" \ && pip install -U pip wheel \ && pip install powerline-status fi .PHONY: misc
注意变量引用要用$${venvPath},防止Make把它当作自身变量解析,交给shell处理该变量。
内容的提问来源于stack exchange,提问作者Thesevs SCUTULATUS
相关产品推荐
相关产品推荐

