让Make目标依赖于二进制,这可能存在于$ PATH上,也可能不存在

时间:2017-08-31 03:19:02

标签: makefile dependencies target

我想添加一个使用envdir运行程序的Make目标。

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” - 我遇到了我依赖的各种不同二进制文件这个问题,这恰好是一个容易分享的例子。

2 个答案:

答案 0 :(得分: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 :(得分:0)

对于"空"问题你可以这样做:

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

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