让Make目标依赖于二进制文件,该文件可能存在于$PATH中,也可能不存在。

5
我想添加一个Make目标,使用envdir运行程序。
ENVDIR := $(shell command -v envdir)

serve: | $(ENVDIR)
    $(ENVDIR) /usr/local/myservice/env python -m SimpleHTTPServer 8000

这个想法是如果用户没有安装envdir,它将为他们安装。我需要管道字符,因为我不总是希望它运行;我只希望有任何版本存在。不幸的是,我无法很好地预测envdir将安装到哪里。

UNAME := $(shell uname)
$(ENVDIR):
ifeq ($(UNAME), Darwin)
    brew install daemontools
endif
ifeq ($(UNAME), Linux)
    sudo apt-get install daemontools
endif
# Add other operating systems here as appropriate...

我遇到的另一个问题是,如果用户没有安装envdir,则该变量将求值为空字符串。因此:a)我的“安装envdir”目标实际上未运行,b)在它运行之后,$(ENVDIR)仍设置为空字符串,因此在服务目标中加载它不起作用。
有没有方法:a)即使未定义ENVDIR也可以运行安装目标,b)重新定义变量的值在“安装envdir”目标运行结束时?
请不要回答“为什么要使用envdir”——我遇到了各种依赖于不同二进制文件的问题,这只是一个容易共享的示例。

为什么不使用条件语句来区分已安装 envdir 和未安装的情况呢?可以使用 ifeq ($(ENVDIR),)...else...endif。在 envdir 安装配方的末尾,您可以简单地重新调用 make 而不是尝试更改 ENVDIR 变量的值(我知道,递归 make 是不好的,但有时候坏事非常有用)。 - Renaud Pacalet
2个回答

1
您可以完全在 shell 中执行此操作,如下所示:

ENVDIR := $( command -v envdir 2> /dev/null || ( install envdir > /dev/null && echo newEnvDirPath ) )

这是最简单的方法,但会导致即使当前目标不需要也安装envdir。如果你想在一个目标中使用它,可以尝试以下方式:
ENVDIR:=$(cat .ENVDIR 2> /dev/null)
$(type -v $(ENVDIR) > /dev/null || rm .ENVDIR )

.ENVDIR:
    command -v envdir 2> /dev/null > $@ || ( install envdir > /dev/null && echo newEnvDirPath > $@)
    $(eval ENVDIR:=$$(cat $@))

shell: | .ENVDIR

如果文件.ENVDIR不存在,它将运行配方,该配方将写入已安装路径到.ENVDIR,或者它将安装并将新路径写入.ENVDIR。无论哪种方式,当配方行完成时,.ENVDIR将包含可执行路径。然后目标运行$(eval)来设置ENVDIR。请注意,调用eval会导致makefile重新解析,这不是理想的,但应该只在系统上发生一次。(还要注意cat命令前面的双$$,这可以防止make在读取时扩展它...)。
或者,如果.ENVDIR存在,则将读取该文件以确定要尝试使用哪个envdir。接下来的一行进行检查,以确保该版本仍然存在于系统上,如果不存在,则删除该文件,强制重新创建。

1
针对“空”的问题,您可以这样做:

ENVDIR := $(or $(shell command -v envdir),envdir)

如果shell函数扩展为空字符串,则将替换为envdiror函数在GNU make 3.81中添加)。这可能足以解决您的所有问题,因为安装后envdir命令现在将出现在PATH上,因此您不需要完整路径。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接